Java中的NIO详解Day12-Java NIO与IO的比较

Java NIO与IO

  • 学习了Java NIOIOAPI后:
    • 何时使用IO, 何时使用NIO
    • Java NIOIO的差异
    • Java NIOIO的使用场景
    • Java NIOIO如何影响代码设计
  • 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 NIOIO调用的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() 方法被调用之前状态相同.否则,下一个读入缓冲区的数据可能无法读到正确的位置
        • 如果缓冲区已满,可以被处理

用来处理数据的线程数

  • Java IO是一个连接通过一个线程处理
    • 如果有少量的连接使用非常高的带宽,一次发送大量的数据,使用一个典型的IO服务器设计会非常契合
  • Java NIO是使用单个线程管理多个连接
    • Java NIO可以只使用一个或者几个单线程管理多个通道,网络连接或者文件
    • 但是解析数据可能会比从一个阻塞流中读取数据更复杂
      • 如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,比如聊天服务器.这时使用实现NIO的服务器就能体现出优势
      • 如果需要维持许多打开的连接到其余计算机上,比如P2P网络中,使用一个单独的线程来管理所有出站连接,这是NIO服务器的一个优势

猜你喜欢

转载自blog.csdn.net/JewaveOxford/article/details/107784175