引自:http://blog.iluckymeeting.com/2018/02/02/JavaNioOverview/
Java NIO简介
Java 1.4开始引入了Java NIO以替换标准Java IO,标准Java IO是基于流(包括byte stream和character stream)的阻塞IO,而Java NIO是基于Channel和Buffer的非阻塞IO.
Java NIO中所有的IO操作都是基于Channel和Buffer的 Java NIO读写数据Java NIO的所有IO操作都是非阻塞的,当线程要读取IO数据时,Channel会把数据读入Buffer,在读入的过程中线程不用阻塞等待,可以去处理别的任务,当数据读入完成后再回来继续处理;同理,当线程要写入数据时,Buffer中的数据被写入Channel,在写入的过程中线程不用阻塞。
Java NIO有三个核心构件:
- Channel
- Buffer
- Selector
Java NIO中的几个核心Channel:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
Buffer支持全部的基本类型byte、char、double、float、int、long、short,对应的提供了
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
一个线程要管理多个Channel连接就要依赖Selector,当然这种情况下一般是有多个打开的连接,并且各连接要处理的IO事件都比较轻量化
Thread-Selector-ChannelChannel创建后会注册到Selector上,Selector的select()方法调用后会阻塞,等待注册的多个Channel有事件到达,然后select()方法返回,线程继续处理。
Java NIO与Java IO比较
Java IO | Java NIO |
---|---|
基于Stream | 基于Buffer |
阻塞IO | 非阻塞IO |
Selector |
基于Stream Vs. 基于Buffer
Java IO是基于Stream的,数据读取操作是由Stream中读取一个或多个byte,每个byte只能由Stream中读取一次,如果要重复使用读取的数据只能是将Stream读出的数据缓存起来。
Java NIO是基于Buffer的,数据读取操作会将Channel中的数据读入Buffer,之后线程可以从任意位置开始使用Buffer里的数据,并且可以重复读取Buffer里的数据,方便灵活。处理Buffer里的数据有两个注意点:
- Buffer里的数据是否是全部数据,也就是是否所有数据都已经由Channel中读入Buffer
- Buffer是否已被填满,如果Buffer已被填满的情况下再往里写入数据,则之前写入的数据会被覆盖
阻塞IO Vs. 非阻塞IO
Java IO基于Stream的读写操作是阻塞的,发起读写操作后线程会阻塞直到读写完成;Java NIO基于Channel和Buffer的读写操作是非阻塞的,发起读操作时,数据会由Channel读入Buffer,发起写操作时,数据会由Buffer写入Channel,线程不必阻塞等待。
Selector
Java NIO之所以能做到非阻塞读写操作,是因为有Selector的存在,当Channel连接建立时会注册到Selector上,线程通过Selector来操作Channel完成数据读写,Selector阻塞在select()方法上,当有Channel达到就绪状态时,Selector会选取这个Channel进行读写操作,这个过程中线程不必阻塞,可以同时去处理别的任务。
Java IO使用 Vs. Java NIO使用
假设要传输以下内容:
Name:张三
Age:34
Addr:北京市
Java IO的处理是:
InputStream input = ... ; // get the InputStream from the client socket
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String nameLine = reader.readLine();
String ageLine = reader.readLine();
String addrLine = reader.readLine();
Java IO读操作首先会阻塞在第一个reader.readLine()操作上,直到第一行数据读取完成,接着阻塞到第二个reader.readLine()方法上,直到第二行数据读取完成,接着阻塞在第三个reader.readLine()方法上,直到第三行数据读取完成。由此可见Java IO操作虽然是阻塞的,但是可以明确的知道数据读取的进度和内容,数据处理逻辑相对简单。
Java NIO的处理是:
ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);
while(! bufferFull(bytesRead) ) {
bytesRead = inChannel.read(buffer);
}
当inChannel.read(buffer)被调用时,只能确定Channel中有数据读入了Buffer,但是并不知道是否所有数据都已经读入了Buffer中,只好不断检测bufferFull()方法,直到所有Buffer被填满.
由此可见Java NIO的读写操作虽然不会阻塞线程,但是数据处理逻辑相对较复杂。
综上所述,Java NIO的非阻塞特性可以让一个线程轻松管理多个Channel连接,但是数据处理的代价较Java IO要更大,所以如果要管理大量连接并且每个连接要处理的数据量不大,则可以选择Java NIO;如果连接数量不多,但是每个连接占用的带宽很大,则可以选择Java IO。
网友评论