首页 > 编程知识 正文

java aqs原理面试,并发编程的三大特性

时间:2023-05-04 12:59:44 阅读:27679 作者:779

Java I/O型号

1 bio (阻塞io ) )。

BIO是同步阻塞模型,其中一个客户端连接对应于一个处理线程。 在BIO中,accept和read方法都是阻止操作,如果没有连接请求,accept方法将被阻止; 如果没有可读数据,read方法将被阻止。

照片

2无阻塞IO (NIO )。

NIO是一种同步无阻塞模型,服务端一个线程可以处理多个请求。 客户端发送的连接请求在多路复用器选择器中注册,服务端线程轮询多路复用器以检查是否存在IO请求,如果有,则进行处理。

NIO的三个主要核心组件:

Buffer :用于存储数据,基础是基于数组实现的,提供了八种基本类型的缓冲类。

Channel :用于进行数据传输,面向缓冲区操作,支持双向传输。 数据可以从通道读取到缓冲区,也可以从缓冲区写入通道。

选择器:选择器。 在一个Selector中注册Channel后,Selector内部机制会自动不断询问这些注册Channel是否有准备好的I/O事件。 例如,可读、可写、网络连接完成等。 这样,当在一个可以轻松使用线程有效管理的通道上发生读或写事件时,该通道将处于就绪状态,并被选择器拦截。 然后,可以使用SelectionKeys获取就绪通道的集合,并进行后续的I/O操作。

照片

Epoll是Linux上多路复用的IO接口select/poll的扩展,在许多并发连接中程序处于少量活动状态时大大提高了系统CPU利用率。 检索事件时,只需要遍历在内核IO事件中异步唤醒并加入Ready队列的描述符集,而不需要遍历整个要调试的描述符集。

是3AIO(niO2.0 )

AIO是一种异步无阻塞模型,通常用于连接数大、连接时间长的APP应用,读写事件完成后,回调服务会通知程序启动线程进行处理。 与NIO不同,读写时,只需直接调用read或write方法。 这两种方法都是异步的,对于读取操作,如果存在可读流,OS会将可读流流传输到read方法的缓冲区,并通知APP应用程序。 对于写入,操作系统将在写入由write方法传递的流完成后主动通知APP应用程序。 所有read/write方法都是异步的,完成后可以理解为主动调用回调函数。

I/O模型的发展

1传统I/O机型

在传统的I/O通信方法中,客户端连接到服务端,服务端接收和响应客户端请求的过程是读取-解码- APP应用程序处理-编码-发送结果。 服务端为每个客户端连接创建新线程,建立通道,以处理后续请求(即BIO )。

这种方式在客户端数量增加的情况下,对连接和请求的响应急剧下降,过于多线程而浪费资源,线程数量并不是没有上限,而是面临各种瓶颈。 虽然可以使用线程池进行优化,但仍然存在许多问题,例如线程池中的所有线程在处理请求时无法响应其他客户端的连接。 此外,每个客户端都需要专用的服务端线程,即使没有请求也不会释放阻塞状态。 在此基础上,提出了事件驱动的Reactor模型。

2反射器型号

Reactor模式由事件驱动开发,用于处理服务器端程序接收到的复用请求,并将它们同步分配给与请求对应的处理线程。 Reactor模式,也称为Dispatcher模式,通过I/O复用批量监听事件,在收到事件后分发。 这是创建高性能网络服务器的必要技术之一。

Reactor模型基于NIO,核心组件包括Reactor和处理程序。

Reactor:Reactor在另一个线程上运行,负责接收和分发事件,并将事件分发到相应的处理程序以响应I/O事件。 它就像公司的电话接线员,负责接听客户的电话,并将线路转发给相应的联系人。

处理程序:处理程序执行I/O事件完成的实际事件,Reactor通过调度相应的处理程序来响应I/O事件,处理程序执行无阻塞操作。 类似于客户想与之对话的公司的实际员工。

根据Reactor的数量和Handler线程的数量,Reactor可以分为三种模型:

单线程模型(单Reactor单线程) )。

多线程模型(单Reactor多线程) ) )。

主从多线程模型(多线程多线程) )。

单线程模型

Reactor内部在Selector中监视连接事件,收到事件后在dispatch中分发,如果是连接建立的事件则由Acceptor处理,Acceptor在accept中接受连接,处理连接后的各种事件的hceptor

处理程序完成读取- -解码-计算-编码- -发送的业务流程。

>

这种模型好处是简单,坏处却很明显,当某个Handler阻塞时,会导致其他客户端的handler和accpetor都得不到执行,无法做到高性能,只适用于业务处理非常快速的场景,如redis读写操作。

主线程中,Reactor对象通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立事件,则由Acceptor处理,Acceptor通过accept接收连接,并创建一个Handler来处理后续事件,而Handler只负责响应事件,不进行业务操作,也就是只进行read读取数据和write写出数据,业务处理交给一个线程池进行处理。

线程池分配一个线程完成真正的业务处理,然后将响应结果交给主进程的Handler处理,Handler将结果send给client。

单Reactor承担所有事件的监听和响应,而当我们的服务端遇到大量的客户端同时进行连接,或者在请求连接时执行一些耗时操作,比如身份认证,权限检查等,这种瞬时的高并发就容易成为性能瓶颈。

存在多个Reactor,每个Reactor都有自己的Selector选择器,线程和dispatch。

主线程中的mainReactor通过自己的Selector监控连接建立事件,收到事件后通过Accpetor接收,将新的连接分配给某个子线程。

子线程中的subReactor将mainReactor分配的连接加入连接队列中通过自己的Selector进行监听,并创建一个Handler用于处理后续事件。

Handler完成read -> 业务处理 -> send的完整业务流程。

关于Reactor,最权威的资料应该是Doug Lea大神的Scalable IO in Java,有兴趣的同学可以看看。

三 Netty线程模型

Netty线程模型就是Reactor模式的一个实现,如下图所示:

image.png

1 线程组

Netty抽象了两组线程池BossGroup和WorkerGroup,其类型都是NioEventLoopGroup,BossGroup用来接受客户端发来的连接,WorkerGroup则负责对完成TCP三次握手的连接进行处理。

NioEventLoopGroup里面包含了多个NioEventLoop,管理NioEventLoop的生命周期。每个NioEventLoop中包含了一个NIO Selector、一个队列、一个线程;其中线程用来做轮询注册到Selector上的Channel的读写事件和对投递到队列里面的事件进行处理。

Boss NioEventLoop线程的执行步骤:

处理accept事件, 与client建立连接, 生成NioSocketChannel。

将NioSocketChannel注册到某个worker NIOEventLoop上的selector。

处理任务队列的任务, 即runAllTasks。

Worker NioEventLoop线程的执行步骤:

轮询注册到自己Selector上的所有NioSocketChannel的read和write事件。

处理read和write事件,在对应NioSocketChannel处理业务。

runAllTasks处理任务队列TaskQueue的任务,一些耗时的业务处理可以放入TaskQueue中慢慢处理,这样不影响数据在pipeline中的流动处理。

Worker NIOEventLoop处理NioSocketChannel业务时,使用了pipeline (管道),管道中维护了handler处理器链表,用来处理channel中的数据。

2 ChannelPipeline

Netty将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipline中流动和传递。ChannelPipeline持有I/O事件拦截器ChannelHandler的双向链表,由ChannelHandler对I/O事件进行拦截和处理,可以方便的新增和删除ChannelHandler来实现不同的业务逻辑定制,不需要对已有的ChannelHandler进行修改,能够实现对修改封闭和对扩展的支持。

ChannelPipeline是一系列的ChannelHandler实例,流经一个Channel的入站和出站事件可以被ChannelPipeline 拦截。每当一个新的Channel被创建了,都会建立一个新的ChannelPipeline并绑定到该Channel上,这个关联是永久性的;Channel既不能附上另一个ChannelPipeline也不能分离当前这个。这些都由Netty负责完成,而无需开发人员的特别处理。

根据起源,一个事件将由ChannelInboundHandler或ChannelOutboundHandler处理,ChannelHandlerContext实现转发或传播到下一个ChannelHandler。一个ChannelHandler处理程序可以通知ChannelPipeline中的下一个ChannelHandler执行。Read事件(入站事件)和write事件(出站事件)使用相同的pipeline,入站事件会从链表head 往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。

3 异步非阻塞

写操作:通过NioSocketChannel的write方法向连接里面写入数据时候是非阻塞的,马上会返回,即使调用写入的线程是我们的业务线程。Netty通过在ChannelPipeline中判断调用NioSocketChannel的write的调用线程是不是其对应的NioEventLoop中的线程,如果发现不是则会把写入请求封装为WriteTask投递到其对应的NioEventLoop中的队列里面,然后等其对应的NioEventLoop中的线程轮询读写事件时候,将其从队列里面取出来执行。

读操作:当从NioSocketChannel中读取数据时候,并不是需要业务线程阻塞等待,而是等NioEventLoop中的IO轮询线程发现Selector上有数据就绪时,通过事件通知方式来通知业务数据已就绪,可以来读取并处理了。

每个NioSocketChannel对应的读写事件都是在其对应的NioEventLoop管理的单线程内执行,对同一个NioSocketChannel不存在并发读写,所以无需加锁处理。

使用Netty框架进行网络通信时,当我们发起I/O请求后会马上返回,而不会阻塞我们的业务调用线程;如果想要获取请求的响应结果,也不需要业务调用线程使用阻塞的方式来等待,而是当响应结果出来的时候,使用I/O线程异步通知业务的方式,所以在整个请求 -> 响应过程中业务线程不会由于阻塞等待而不能干其他事情。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。