所有文章
https://www.cnblogs.com/lay2017/p/12901123.html
正文
我们什么时候该使用IO,什么时候该使用NIO呢?
本文会罗列一些NIO和IO的不同点,和用法,以及它们都是如何影响设计的。
主要的不同点
IO | NIO |
面向流 | 面向缓冲区 |
阻塞IO | 非阻塞IO |
无选择器 | 有选择器 |
面向流 VS 面向缓冲区
面向流,意味着你将从一个流中有序地读取字节数据,你无法在流中前后移动操作位置。如果你想这么做,你必须把流全部读取出来,然后存储到一个buffer里面,继而前后移动操作位置。
面向缓冲区就不同了,你可以在buffer上移动操作的位置。但是,面向缓冲区意味着你需要去判断哪些数据能够形成一套完整的信息。以及,你要确保新进入的数据不会覆盖了旧数据。
阻塞 VS 非阻塞
阻塞意味着当一个线程调用了read或者write,这个线程就不能做其它事情,必须等待io两阶段都结束。
非阻塞就不同了,一个线程可以控制多个IO,当IO可用的时候线程再进行处理。线程可以做其它事情。
选择器
选择器是NIO独有的,选择器可以监控多个channel,使得单线程可以控制多个IO的输入输出。
NIO和IO如何影响程序设计
无论你选择哪种方式,将会影响以下三个方面
1.调用NIO或者IO的API类
2.数据的处理过程
3.用于处理数据的线程数量
API调用
NIO和IO的API调用当然是不同的,这点毋庸置疑。
数据处理
在IO中,你将从流里面读取数据,例如你将逐行读取
Name: Anna Age: 25 Email: anna@mailserver.com Phone: 1234567890
这个文本流在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 emailLine = reader.readLine(); String phoneLine = reader.readLine();
readLine方法返回的就会是一行数据,在此之前会阻塞等待,线程也不能做别的事,如图
如果是NIO的实现
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
read方法会立即返回,bytesRead可能为0,也可能大于0。也就是说数据可能不会一次性读完,所以你需要循环调用
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer); while(! bufferFull(bytesRead) ) { bytesRead = inChannel.read(buffer); }
bufferFull方法校验是否构成了一个完整的数据信息,流程如图
汇总
NIO使得你可以单线程管理多个channel,但是也导致数据解析的复杂度上升。
如果你需要并发很多连接,每个连接传输的数据量较小,例如聊天室程序,使用NIO是非常正确的。一个线程管理多个连接如图
如果连接非常少,但是传输的数据非常大,那么采用传统IO比较合适,如图