Java BIO/NIO/AIO 学习
引言
本文主要介绍Java 语言IO 相关介绍。
由上描述基本可以总结一句简短的话,同步和异步是目的,阻塞和非阻塞是实现方式。
- 同步:用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪。(自己上街买衣服,自己亲自干这件事,别的事干不了)
- 异步:用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。(告诉朋友自己合适衣服的尺寸,大小,颜色,让朋友委托去卖,然后自己可以去干别的事)
- 阻塞:当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止。(去公交站充值,发现这个时候,充值员不在(可能上厕所去了),然后我们就在这里等待,一直等到充值员回来为止)
- 非阻塞:非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待。(去点餐,排队领取小票,领取完后我们自己可以玩玩手机,或者与别人聊聊天,当轮我们时,银行的喇叭会通知)
从编程语言层面
BIO:同步阻塞IO,一个连接一个线程
NIO:同步非阻塞IO,一个请求一个线程
AIO:异步非阻塞IO,一个有效请求一个线程
BIO
在JDK1.4之前,用Java编写网络请求,都是建立一个ServerSocket,然后,客户端建立Socket时就会询问是否有线程可以处理,如果没有,要么等待,要么被拒绝。即:一个连接,要求Server对应一个处理线程。
NIO
在JDK1.4及以后版本中提供了一套API来专门操作非阻塞I/O,我们可以在java.nio包及其子包中找到相关的类和接口。由于这套API是JDK新提供的I/O API,因此,也叫New I/O,这就是包名nio的由来。这套API由三个主要的部分组成:缓冲区(Buffers)、通道(Channels)和非阻塞I/O的核心类组成。
NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。
也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。
AIO
在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道,其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。
AIO的实现
在windows上,AIO的实现是通过IOCP来完成的,看JDK的源代码,可以发现
WindowsAsynchronousSocketChannelImpl
看实现接口:
implements Iocp.OverlappedChannel
在linux上,AIO的实现是通过epoll来完成的,看JDK源码,可以发现,实现源码是:
UnixAsynchronousSocketChannelImpl
看实现接口:
implements Port.PollableChannel
这是与windows最大的区别,poll的实现,在linux2.6后,默认使用epoll。
IO 多路复用
1. 消息传递方式:
select:内核需要将消息传递到用户空间,需要内核的拷贝动作;
poll:基本与select没有区别;
epoll:通过内核和用户空间共享一块内存来实现
2. 文件句柄剧增后到来的IO效率问题
select:因为每次调用都会对连接进行遍历,所以锁着剧增会造成遍历速度下降;
poll:基本与select没有区别
epoll:通过callback函数来实现,只有活跃的socket才会主动调用;如果在活跃socket较少的情况下,不会造成性能下降的问题
3. 支持一个进程能打开的最大连接数
select:在32位机器上,大小是3232;64位机器上是3264;
poll:没有最大连接数,基于链表存储的;
epoll:连接数虽然有上线,但是很大;1G内存的机器上可以打开10W左右的连接