zoukankan      html  css  js  c++  java
  • Java NIO的工作方式

    NIO的工作机制

    为了了解NIO,我们先看一下NIO的相关类图,如下图所示:

    上图中有两个关键类Channel和Selector,他们是Java NIO的核心。举个例子,我们把Channel比作高铁,则Selector就是高铁的调度系统,负责监控每列高铁的运行状态,是出站还是在路上,也就是说Selector可以轮询Channel的状态。还有一个Buffer类,可以将它比作高铁上的座位,至于是一等座还是二等座我们不得而知。了解了上面的例子,我们具体看一下NIO是如何工作的,下面是一段典型的NIO代码:

    public void selector() throws IOException {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置非阻塞方式
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(80));
            //注册监听事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while(true){
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> its = keys.iterator();
                while(its.hasNext()){
                    SelectionKey selectionKey = its.next();
                    if((selectionKey.readyOps() & SelectionKey.OP_ACCEPT) ==SelectionKey.OP_ACCEPT){
                        ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                        //接收到请求
                        SocketChannel socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                        its.remove();
                    }else if((selectionKey.readyOps() & SelectionKey.OP_READ) ==SelectionKey.OP_READ){
                        SocketChannel sc = (SocketChannel) selectionKey.channel();
                        while(true){
                            buffer.clear();
                            int n = sc.read(buffer);
                            if(n<=0){
                                break;
                            }
                            buffer.flip();
                        }
                        its.remove();
                    }
                }
            }
        }
    

    调用Selector的静态方法创建一个Selector,创建一个服务端的Channel绑定到Socket对象上,把这个通信信道注册到选择器上并设置为非阻塞模式。然后调用Selector的selectedKeys方法检查注册到选择器上的所有通信信道是否有感兴趣的事件发生。如果有事件发生则返回所有的SelectionKey,通过SelectionKey的channel方法可以取得通信信道,从而读取通信数据。

    上图描述了基于NIO的Socket请求处理过程。Selector可以同时监听一组通信信道上的IO状态,前提是这些通信信道已经注册到Selector上。Selector可以调用select方法检查通信信道上的IO是否已经准备好,如果监听的所有通信信道上没有状态变化,那么select方法会阻塞或则超时返回0。如果有多个通信信道有数据,则把这些数据分配到对应的Buffer中。所以关键的地方是,有一个线程处理所有链接的数据交互,每个链接的数据交互都不是阻塞的,所以可以同事处理大量的请求。

    Buffer的工作方式

    可以把Buffer理解为一组基本数据类型的元素列表 ,它通过几个变量保存这些数据的当前位置状态,也就是有四个索引。

    • capacity:缓存区数据的总长度
    • position:下一个要操作的元素的位置
    • limit:缓存区中不可操作的下一个元素的位置limit<=capacity
    • mark:用于记录当前position的前一个位置或则默认是0

    他们的关系如下图:

    我们通过ByteBuffer.allocate(11)创建一个11个字节的数组缓存区,出事状态下position为0,capacity和limit都是默认长度,如上图。

    当我们向Buffer中添加五个元素后,position的值变为5,而limit和capacity的值不变,如下图:

    当我们调用buffer.flip()方法后,position变为0,limit变为5,capacity不变,如下图:这时候底层操作系统就可以从Buffer中读取5个字节并发射出去。

    当下一次写数据之前,调用clear方法,缓冲区的索引状态又回到初始状态。当我们调用mark()方法的时候,他将记录当前position的前一个位置,调用reset后,position将恢复mark记录下的位置。

    有一点需要说明,通过Channel获取的IO数据首先要经过操作系统的Socket缓冲区,再将数据复制到Buffer中,这个操作系统缓冲区就是TCP关联的RECEQ和SENDQ队列,从操作系统缓冲区到用户缓冲区的数据复制比较耗性能,BUffer提供了一种直接操作操作系统缓冲区的方式ByteBuffer.allocateDirect,这个方法返回的DirectByteBuffer就是关联的底层操作系统缓冲区,他通过Native代码操作非JVM堆的内存空间,每次的创建和释放都调用System.gc,容易引起JVM内存泄露问题。

    NIO数据访问方式

    NIO提供过了比传统的文件访问方式更好的文件访问方式

    • FileChannel.transferFrom,FileChannel.transferTo
    • FileChannel.map

    FileChannel.transferXXX

    FileChannel.transferXXX相比传统方法可以减少从内核到用户空间的复制,数据直接在内核中移动,在Linux中使用的是sendfile系统调用。

    FileChannel.map

    FileChannel.map按照一定的大小块把文件映射为内存区域,程序访问内存区域的时候,直接访问文件系统,这种方式省去了从内核空间到用户空间的复制的损耗,适合对大文件的只读操作。但是这种方式与操作系统的底层实现有关。

  • 相关阅读:
    优先队列
    Problem W UVA 662 二十三 Fast Food
    UVA 607 二十二 Scheduling Lectures
    UVA 590 二十一 Always on the run
    UVA 442 二十 Matrix Chain Multiplication
    UVA 437 十九 The Tower of Babylon
    UVA 10254 十八 The Priest Mathematician
    UVA 10453 十七 Make Palindrome
    UVA 10163 十六 Storage Keepers
    UVA 1252 十五 Twenty Questions
  • 原文地址:https://www.cnblogs.com/senlinyang/p/8253223.html
Copyright © 2011-2022 走看看