一、IO
(一)字节流(8bit)
InputStream、Outputstream。
按字节读写,不会用到缓存。
只是读写文件,和文件内容无关的,一般选择字节流。
(二)字符流(16 bit)
reader、writer
按字符读写,用到了缓存。
在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。
二、NIO
NIO 是一种同步非阻塞的 IO 模型,面向缓冲。
同步:线程不断轮询 IO 事件是否就绪
非阻塞:指线程在等待 IO 的时候,可以同时做其他任务。
(一)核心部分
-
Channels
-
Buffers
-
Selectors
(二)缓冲区Buffer
缓冲区有直接缓冲区和非直接缓冲区之分,它实际上也是一段内存空间。在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的。
(三)通道Channel
通道是一个对象,通过通道可以读取和写入数据,所有数据都通过Buffer对象来处理。
Channel和传统IO中的Stream很相似。主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作;
(四)选择器Selector
同步的核心就是 Selector,Selector 代替了线程本身轮询 IO 事件,避免了阻塞同时减少了不必要的线程消耗;
Selector选择器提供了选择已经就绪的任务的能力。Selector会不断的轮询注册在上面的所有channel,如果某个channel为读写等事件做好准备,那么就处于就绪状态,通过Selector可以不断轮询发现出就绪的channel,进行后续的IO操作。一个Selector能够同时轮询多个channel。这样,一个单独的线程就可以管理多个channel,从而管理多个网络连接。这样就不用为每一个连接都创建一个线程,同时也避免了多线程之间上下文切换导致的开销。
三、区别
IO | NIO |
面向流 | 面向缓冲 |
阻塞IO | 非阻塞IO |
无 | 选择器 |
NIO和IO适用场景
因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。所以每次数据处理之前都要检测缓冲区数据。
那么NIO和IO各适用的场景是什么呢?
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。
而如果只有少量的连接,而这些连接每次要发送大量的数据,这时候传统的IO更合适。