zoukankan      html  css  js  c++  java
  • Java NIO系列教程(一) Java NIO 概述

    一、阻塞IO与非阻塞IO

    阻塞IO:

      通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的Server/Client模式会基于TPR(Thread per Request),服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型,并设置线程池线程的最大数量,这由带来了新的问题,如果线程池中有200个线程,而有200个用户都在进行大文件下载,会导致第201个用户的请求无法及时处理,即便第201个用户只想请求一个几KB大小的页面。传统的 Server/Client模式如下图所示:

    非阻塞IO(NIO):

      NIO中非阻塞I/O采用了基于Reactor模式的工作方式,I/O调用不会被阻塞,相反是注册感兴趣的特定I/O事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们。NIO中实现非阻塞I/O的核心对象就是Selector,Selector就是注册各种I/O事件地 方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件,如下图所示:

    从图中可以看出,当有读或写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从 SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。

    非阻塞指的是IO事件本身不阻塞,但是获取IO事件的select()方法是需要阻塞等待的.区别是阻塞的IO会阻塞在IO操作上, NIO阻塞在事件获取上,没有事件就没有IO, 从高层次看IO就不阻塞了.也就是说只有IO已经发生那么我们才评估IO是否阻塞,但是select()阻塞的时候IO还没有发生,何谈IO的阻塞呢?NIO的本质是延迟IO操作到真正发生IO的时候,而不是以前的只要IO流打开了就一直等待IO操作。

    二.NIO原理及通信模型

    Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:

    1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。 
    2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。 
    3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。 

    阅读过一些资料之后,下面贴出我理解的java NIO的工作原理图:

     

     

    (注:每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。)

    Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:

     

    事件名 对应值
    服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
    客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
    读事件 SelectionKey.OP_READ(1)
    写事件 SelectionKey.OP_WRITE(4)

     服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:

    三、Java NIO 概述

    Java NIO 由以下几个核心部分组成:

    • Channels
    • Buffers
    • Selectors

    虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类。因此,在概述中我将集中在这三个组件上。其它组件会在单独的章节中讲到。

     

    Channel 和 Buffer

    基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

    Channel和Buffer有好几种类型。下面是JAVA NIO中的一些主要Channel的实现:

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel

    正如你所看到的,这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。

    与这些类一起的有一些有趣的接口,但为简单起见,我尽量在概述中不提到它们。本教程其它章节与它们相关的地方我会进行解释。

    以下是Java NIO里关键的Buffer实现:

    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

    这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。

    Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件, 我也不打算在概述中说明。

    Selector

    Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

    这是在一个单线程中使用一个Selector处理3个Channel的图示:

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

  • 相关阅读:
    关闭编辑easyui datagrid table
    sql 保留两位小数+四舍五入
    easyui DataGrid 工具类之 util js
    easyui DataGrid 工具类之 后台生成列
    easyui DataGrid 工具类之 WorkbookUtil class
    easyui DataGrid 工具类之 TableUtil class
    easyui DataGrid 工具类之 Utils class
    easyui DataGrid 工具类之 列属性class
    oracle 卸载
    “云时代架构”经典文章阅读感想七
  • 原文地址:https://www.cnblogs.com/dengyungao/p/7550455.html
Copyright © 2011-2022 走看看