- 基本概念
- 同步:多个任务之间必须有序的执行
- 异步:多个任务之间可以独立的执行,互不干扰
- 阻塞:当条件不满足时,程序会卡在那里,直至条件满足为止
- 非阻塞:当条件不满足时,程序不会卡在那里,同时会收到一个标志信息告知当前条件不满足
- 同步和异步着重点在于多个任务执行过程中,后发起的任务是否必须等先发起的任务完成之后再进行
- 阻塞和非阻塞重点在于请求的方法是否立即返回(或者说是否在条件不满足时被阻塞)
- IO模型
- BIO(blocking I/O):阻塞IO
- NIO(non-blocking I/O):非阻塞IO
- AIO(Asynchronous I/O):异步IO
- BIO模型(同步阻塞IO模型)
- 一个连接对应一个线程:每当有客户端建立连接时,就为其创建一个线程
- 缺点:如果同时有大量客户端接入,系统会创建大量线程,线程数量的快速膨胀会使系统性能急剧下降,甚至系统崩掉
- 伪异步I/O模型(同步阻塞IO模型)
- BIO模型的优化版,将线程的创建交由连接池取管理,可以节约系统资源
- 缺点:当同时有大量客户端接入时,因为线程池有线程上限限制,会出现排队的情况,引发连接失败或超时
- Reactor模式
- 核心思想:分而治之+事件驱动
- 分而治之:将一次完整网络请求的处理任务分为几个子任务
- 事件驱动:每个子任务对应一个事件,当子任务准备就绪就发出事件信号,Reactor管理器收到信号就调用相应的事件处理器处理这个子任务
- NIO模型(同步非阻塞IO)
- NIO模型的基础:Selector(多路复用器)
- NIO模型基于Reactor模式
- 三大核心组件:缓冲区(Buffer)、通道(Channel)、选择器(Selector)
- 当客户端接入时, Reactor会将accept事件分发给Acceptor处理器进行处理
- 当收到读写请求时,Reactor会根据事件类型调用相应的事件处理器进行处理
- 缺点:不能利用多核CPU,一个线程需要执行处理所有的accept、read、decode、process、encode、send事件,如果其中decode、process、encode事件的处理很耗时,则服务端无法及时响应其他客户端的请求事件
- 对Reactor单线程模式的一种优化,Reactor只需要专心监听处理客户端请求事件accept、read、write;
- 使用线程池执行数据的具体处理过程decode、process、encode,提高数据处理过程的响应速度;
- 缺点:因为Reactor仍是单线程,无法并行响应多个客户端的请求事件(比如同一时刻只能read一个客户端的请求数据)
- mainReactor 是一个独立的NIO线程池, 负责处理链接请求,认证,登陆等操作
- Acceptor处理完成后,将事件注册到subReactor线程池中的某个IO线程上去,此IO线程继续完成后面的IO操作
- Netty采用类似这种模式,boss线程池就是多个mainReactor,worker线程池就是多个subReactor。
- AIO模型(异步非阻塞IO)
- AIO模型基于Proactor模式
- 用户线程直接使用内核提供的异步IO API发起read请求,且发起后立即返回,继续执行用户线程代码
- 操作系统开启独立的内核线程去处理IO操作
- 内核将read的数据和用户线程注册的CompletionHandler分发给内部Proactor,Proactor将IO完成的信息通知给用户线程(一般通过调用用户线程注册的完成事件处理函数),完成异步IO
- java IO和NIO
- IO是面向流的,每次从流(InputStream/OutputStream)中读一个或多个字节,直到读取完所有字节,它们没有被缓存在任何地方。另外,它不能前后移动流中的数据,如需前后移动处理,需要先将其缓存至一个缓冲区
- 各种IO的流都是阻塞式的,当某一线程内调用read()或者write()方法时,当前线程会被阻塞,直到读完或写完数据为止
- NIO面向缓冲,数据会被读取到一个缓冲区,需要时可以在缓冲区中前后移动处理,这增加了处理过程的灵活性。但与此同时在处理缓冲区前需要检查该缓冲区中是否包含有所需要处理的数据,并需要确保更多数据读入缓冲区时,不会覆盖缓冲区内尚未处理的数据。
- NIO为非阻塞模式。读写请求并不会阻塞当前线程,在数据可读/写前当前线程可以继续做其它事情,所以一个单独的线程可以管理多个输入和输出通道
- 零拷贝:NIO中提供的FileChannel拥有transferTo和transferFrom两个方法,可直接把FileChannel中的数据拷贝到另外一个Channel,或者直接把另外一个Channel中的数据拷贝到FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。在操作系统支持的情况下,通过该方法传输数据并不需要将源数据从内核态拷贝到用户态,再从用户态拷贝到目标通道的内核态,同时也避免了两次用户态和内核态间的上下文切换,也即使用了“零拷贝”,所以其性能一般高于Java IO中提供的方法。
参考:
- BIO,NIO,AIO 总结 https://github.com/Snailclimb/JavaGuide/blob/master/Java%E7%9B%B8%E5%85%B3/BIO%2CNIO%2CAIO%20summary.md
- Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式 http://www.jasongj.com/java/nio_reactor/
- Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码) https://blog.csdn.net/anxpp/article/details/51512200