本文主要介绍一下BIO、NIO、AIO
Block-IO:基于字节流的InputStream和OutputStream,基于字符流的Reader和Writer,同时也包括Socket那些java.net包下的类等。
BIO的属性是同步阻塞的,优点就是写起来简单缺点是效率太低。
NonBlock-IO:在JDK4中引入了NIO,它是一种构建多路复用的,同步非阻塞的IO操作提供了Selector、Channel、Buffer等新的抽象,同时提供了更底层的数据操作方式。
下面介绍一个NIO的三大核心:Channels,Buffers,Selectors。
NIO-Channel
Channel有点像流,一个的数据都是从Channel开始然后读到Buffer中再从Buffer写到Channel中。
NIO的Channels有以下几种类型,从方法名可以看出有UDP的Channel有文件Channel还有TCP的Channel:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
其中,FileChannel中有两个有意思的方法:
transferTo
:把FileChannel中的数据拷贝到另外一个ChanneltransferFrom
:把另外一个Channel中的数据拷贝到FileChannel
该Channel避免了两次用户态和内核态之间的上下文切换,即“零拷贝”,效率较高。
NIO-Buffer
有以下几种类型,进行缓冲流的数据属性修饰:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
- MappedByteBuffer
NIO-Selector
如果连接打开了多个通道,但是每个通道的流量都很低就可以使用Selector。
另外NIO的底层也采用了IO的多路复用:调用系统级别的select/poll/epoll,意思就是说可以单线程处理多路IO。
然后简单介绍一下三者以及三者的区别:
支持一个线程所能打开的最大连接数
select:单个进程能打开的最大连接数由FD_SETSIZE宏定义,其大小是32个整数的大小,我们可以对其进行修改,然后重新编译内核,但是性能无法保证,需要进一步测试
poll:本质上和select没有区别,但是没有最大连接数的限制,原因是因为他是基于链表去存储的
epoll:虽然连接数由上线,但是很大,1G内存的机器可以打开10万左右的连接
面对文件句柄DF剧增之后也会带来IO效率问题
select:因为每次调用的时候都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度的线性下降的性能问题
poll:同上
epoll:由于epoll是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll不会有线性下降的性能问题,但是所有socket都很活跃的情况下可能会有性能上的问题
在消息的传递方式上
select:内核需要将消息传递到用户空间,需要内核的拷贝工作
poll:同上
通过内核和用户空间共享一块内存来实现性能很高
JDK7又引入了AIO:基于事件和回调机制
AIO属于异步模型,也就是说处理IO的时候可以同时处理别的事情,那我们应该如何进一步加工处理结果呢?
- 基于回调:实现CompletionHandler接口,调用的时候触发回调函数;
- 返回Future:通过isDone()查看是否准备好,通过get()等待返回数据