Java NIO与IO的比较
Java NIO与IO
- 学习了Java NIO和IO的API后:
- 何时使用IO, 何时使用NIO
- Java NIO和IO的差异
- Java NIO和IO的使用场景
- Java NIO和IO如何影响代码设计
- Java NIO和IO的主要区别:
IO | NIO |
---|---|
面向流 | 面向缓冲 |
阻塞IO | 非阻塞IO |
无 | 选择器 |
面向流IO和面向缓冲NIO
- IO是面向流的:
- 每次从流中读取一个或者多个字节,直至读取所有字节,这些字节没有被缓存在任何地方
- 不能前后移动流中的数据.如果需要前后移动从流中读取的数据,需要先将流缓存到一个缓冲区
- NIO是面向缓冲区的:
- 数据读取到一个稍后处理的缓冲区,需要时可以在缓冲区中前后移动,增加了处理过程中的灵活性
- 需要检查该缓冲区中是否包含所有需要处理的数据
- 需要确保当更多数据读入缓冲区时,不会覆盖缓冲区里尚未处理的数据
阻塞模式IO与非阻塞模式NIO
- Java IO中各种流是阻塞的:
- 当一个线程调用read() 方法或者write() 方法时,该线程被阻塞,直到有一些数据被读取或者完全写入
- 该线程在阻塞期间不会进行任何操作
- Java NIO有非阻塞模式:
- 非阻塞读取:
- 使得一个线程从某通道发送请求读取数据,但是仅能得到目前可用的数据,如果没有数据可用时,就什么都不会获取
- 不会保持线程阻塞,所以在数据变得可用之前,该线程可以继续做其余的事情
- 非阻塞写入:
- 使得一个线程请求写入一些数据到某通道,但是不需要等待完全写入,这个线程可以继续做其余的事情
- 线程通常将非阻塞IO的空闲时间用于其余通道上执行IO操作,所以一个单独的线程可以管理多个输入和输出通道Channel
- 非阻塞读取:
Java NIO的选择器Selector
- Java NIO的选择器Selector允许一个单独的线程来监视多个输入通道:
- 可以注册多个通道使用一个选择器,然后使用一个单独的线程来 “选择” 通道
- 这些通道里已经有可以处理的输入
- 或者已准备写入的通道
- 可以注册多个通道使用一个选择器,然后使用一个单独的线程来 “选择” 通道
- Java NIO的这种选择机制使得一个单独的线程很容易来管理多个通道
Java NIO和IO如何影响应用程序的设计
- 无论Java NIO还是IO, 会影响应用程序设计的以下几个方面:
- 对NIO或者IO类的API调用
- 数据处理
- 用来处理数据的线程数
API调用
- Java NIO和IO调用的API不同:
- Java NIO是的数据是读入缓冲区然后处理
- Java IO是从一个InputStream逐字节读取
数据处理
- Java IO是从一个阻塞的流中读取数据
- IO中,从InputStream或者Reader逐字节读取数据
- 示例:
Name: Anna
Age: 25
Email: [email protected]
Phone: 987654321- 该文本行的流的处理:
InputStream input = .... // 从客户端的Socket中获取输入流 BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String nameLine = reader.readLine(); String ageLine = reader.readLine(); String emailLine = reader.readLine(); String phoneLine = reader.readerLine();
- 处理状态由程序执行多久决定:
- 一旦reader.readLine() 方法返回,就可以知道文本行已经读完 ,readline() 阻塞直到整行读完,也可以直到此行包含的数据
- 该处理程序仅在有新数据读入时运行,并不知道每步的数据是什么
- 一旦正在运行的线程处理过读入的某些数据,该线程不会再回退数据
- Java NIO是从一个通道里读取数据,直到所有的数据都读到缓冲区
- Java NIO中,从缓冲区Buffer中读取数据
- 示例:
ByteBuffer byteBuffer = ByteBuffer.allocate(64); int bytesRead = inChannel.read(byetBuffer);
- inChannel.read(byteBuffer) 从通道读取字节到ByteBuffer, 当这个调用方法返回时:
- 不知道所需的所有数据是否在缓冲区内
- 只知道,该缓冲区包含一些字节
- 如何知道该缓冲区是否包含足够的数据可以处理:
- 只能查看缓冲区的数据
- 导致在知道所有数据都在缓冲区之前,必须检查几次缓冲区的数据
- 这样不仅效率低下,而且使得程序设计杂乱不堪
ByteBuffer byteBuffer = ByteBuffer.allocate(64); int bytesRead = inChannel.read(byteBuffer); while (!bufferFull(bytesRead)) { bytesRead = inChannel.read(byteBuffer); }
- bufferFull() 方法必须跟踪有多少数据读入缓冲区,并返回真或假,取决于缓冲区是否已满.如果缓冲区准备好被处理,那么表示缓冲区已经满了
- bufferFull() 方法扫描缓冲区:
- 必须保持在bufferFull() 方法被调用之前状态相同.否则,下一个读入缓冲区的数据可能无法读到正确的位置
- 如果缓冲区已满,可以被处理
- inChannel.read(byteBuffer) 从通道读取字节到ByteBuffer, 当这个调用方法返回时:
用来处理数据的线程数
- Java IO是一个连接通过一个线程处理
- 如果有少量的连接使用非常高的带宽,一次发送大量的数据,使用一个典型的IO服务器设计会非常契合
- Java NIO是使用单个线程管理多个连接
- Java NIO可以只使用一个或者几个单线程管理多个通道,网络连接或者文件
- 但是解析数据可能会比从一个阻塞流中读取数据更复杂
- 如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,比如聊天服务器.这时使用实现NIO的服务器就能体现出优势
- 如果需要维持许多打开的连接到其余计算机上,比如P2P网络中,使用一个单独的线程来管理所有出站连接,这是NIO服务器的一个优势