Java NIO和阻塞IO

来源:互联网 发布:雅思精听 知乎 编辑:程序博客网 时间:2024/05/16 14:51
阻塞IO:
        在阻塞模式下,服务器调用ServerSocket.accept()方法同步监听一个ServerSocket,该方法会一直阻塞到有客户端连接才会返回,当有客户端连接了,服务器会启动与一个专门的线程调用InputStream.read()方法来读取客户端请求,InputStream.read()方法也是阻塞的,如果线程从客户端中读取不到指定大小的数据量,阻塞IO就在那里阻塞着,该线程被挂起不能去做其他事情。比如,已知客户端后面会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就在那傻傻地等到下一个字节的到来,对,就在那等着,啥事也不做,直到把这10个字节读取完,这才将阻塞的处理线程放开通行。阻塞的结果就是会带来大量的进程上下文切换,因为cpu要进行轮询。
        阻塞IO在性能方面是很低下的,如果要使用阻塞IO完成一个Web服务器的话,那么服务器对于每一个请求都必须启用一个线程进行处理
        通信模型图如下所示:

       
非阻塞IO:
        在非阻塞模式下,若从客户端中读取不到指定大小的数据量,非阻塞IO就立即通行。比如,已知后面会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就读取这8个字节的数据,读完后就立即返回,等另外两个字节再来的时候再去读取。
       而使用非阻塞IO的话,一到两个线程基本上就够了,因为线程不会产生阻塞,好比一下接收A请求的数据,另一下接收B请求的数据,等等,就是不停地东奔西跑,直接到把数据接收完了。

        Java NIO不但引入了全新的高效的I/O机制,同时引入了基于Reactor设计模式的多路复用异步模式。NIO包中主要包含以下几种抽象数据类型。
        1. Channel(通道)
            NIO把它支持的I/O对象抽象为Channel。它模拟了通信连接,类似于原I/O中的流(Stream),用户可以通过它读 取和写入数据。
        2. Buffer(缓冲区)
            Buffer是一块连续的内存区域,一般作为Channel收发数据的载体出现。所有数据都通过Buffer对象来处理。
        3. Selector(选择器)
      Selector类提供了监控一个和多个通道当前状态的机制。只要Channel向Selector注册了某种特定的事 件,Selector就会监听这些事件是否会发生,一旦发生某个事件,便会通知对应的Channel。使用选择器,借助单一线程,就可对数量庞大的活动 I/O通道实施监控和维护。 

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


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

         传统的阻塞式IO,每个连接必须要开一个线程来处理,并且没处理完线程不能退出。非阻塞式IO,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理。事件来了就处理,处理完了就把线程归还。而传统阻塞方式不能使用线程池来处理,假设当前有10000个连接,非阻塞方式可能用1000个线程的线程池就搞定了,而传统阻塞方式就需要开10000个来处理。如果连接数较多将会出现资源不足的情况。非阻塞的核心优势就在这里。

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时发 现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。





原创粉丝点击