Java I/O 模型的演进
Java 的 I/O 依赖于操作系统的实现,所以先了解 Unix 的 I/O 模型有助于理解 Java 的 I/O。
相关概念
同步和异步
描述的是用户线程与内核的交互方式:
- 同步是指用户线程发起 I/O 请求后需要等待或者轮询内核 I/O 操作完成后才能继续执行;
- 异步是指用户线程发起 I/O 请求后仍继续执行,当内核 I/O 操作完成后会通知用户线程,或者调用用户线程注册的回调函数
阻塞和非阻塞
描述的是用户线程调用内核 I/O 操作的方式:
- 阻塞是指 I/O 操作需要彻底完成后才返回到用户空间;
- 非阻塞是指 I/O 操作被调用后立即返回给用户一个状态值,无需等到 I/O 操作彻底完成。
一个 I/O 操作其实分成了两个步骤:发起 I/O 请求和实际的 I/O 操作。 阻塞 I/O 和非阻塞 I/O 的区别在于第一步,发起 I/O 请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞 I/O ,如果不阻塞,那么就是非阻塞 I/O 。 同步 I/O 和异步 I/O 的区别就在于第二个步骤是否阻塞,如果实际的 I/O 读写阻塞请求进程,那么就是同步 I/O 。
Unix I/O 模型
Unix 下共有五种 I/O 模型:
- 阻塞 I/O
- 非阻塞 I/O
- I/O 复用(select 和 poll)
- 信 驱动 I/O(SIGIO)
- 异步 I/O(POSIX 的 aio_系列函数)
而Java的I/O模型和unixI/O模型类似
“阻塞I/O”模式
服务器启动后,等待客户端连接。在客户端连接服务器后,服务器就阻塞读写取数据流。
改进为“阻塞I/O+多线程”模式
使用多线程来支持多个客户端来访问服务器。存在问题:每次接收到新的连接都要新建一个线程,处理完成后销毁线程,代价大。当有大量地短连接出现时,性能比较低。
改进为“阻塞I/O+线程池”模式
针对上面多线程的模型中,出现的线程重复创建、销毁带来的开销,可以采用线程池来优化。每次接收到新连接后从池中取一个空闲线程进行处理,处理完成后再放回池中,重用线程避免了频率地创建和销毁线程带来的开销。
存在问题:在大量短连接的场景中性能会有提升,因为不用每次都创建和销毁线程,而是重用连接池中的线程。但在大量长连接的场景中,因为线程被连接长期占用,不需要频繁地创建和销毁线程,因而没有什么优势。
虽然这种方法可以适用于小到中度规模的客户端的并发数,如果连接数超过 100,000或更多,那么性能将很不理想。
改进为“非阻塞I/O”模式
“阻塞I/O+线程池” 络模型虽然比”阻塞I/O+多线程” 络模型在性能方面有提升,但这两种模型都存在一个共同的问题:读和写操作都是同步阻塞的,面对大并发(持续大量连接同时请求)的场景,需要消耗大量的线程来维持连接。CPU 在大量的线程之间频繁切换,性能损耗很大。一旦单机的连接超过1万,甚至达到几万的时候,服务器的性能会急剧下降。
而 NIO 的 Selector 却很好地解决了这个问题,用主线程(一个线程或者是 CPU 个数的线程)保持住所有的连接,管理和读取客户端连接的数据,将读取的数据交给后面的线程池处理,线程池处理完业务逻辑后,将结果交给主线程发送响应给客户端,少量的线程就可以处理大量连接的请求。
Java NIO 由以下几个核心部分组成:
- Channel
- Buffer
- Selector
改进为“异步I/O”模式
Java SE 7 版本之后,引入了异步 I/O (NIO.2) 的支持,为构建高性能的 络应用提供了一个利器。
转自:http://www.importnew.com/21383.html
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!