NIO 简介

Posted xmzjava

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NIO 简介相关的知识,希望对你有一定的参考价值。

上文我们描述了五中IO类型。第一种同步阻塞模型我们我们称之为BIO(Blocking IO), 

技术分享图片

 

第三种IO复用模型我们称之为NIO(Nonblocking IO)。

 技术分享图片

 

 

  上图我们可以很容易的发现 BIO会为每个socket请求创建一个线程,而NIO可以通过一个线程处理多个请求。当然,我们可以为BIO构建一个线程池,这是一种伪异步的BIO模型。BIO和NIO最大的区别还是在阻塞上面。

阻塞主要有两方面

  • 等待网络可读写  server.accept()
  • 读写阻塞

通过观察InputStream的Api我们可以了解到,只有在下面三种情况下,BIO才会解除阻塞

1.有数据可读
2.可用数据已读取完毕
3.发送空指针或者I/O异常

 

所以,假如我们使用BIO进行网络消息传递,在网络不稳定的情况下,一次消息的传递需要花费30s,那这个bio的线程就需要阻塞30秒,假如所有的线程都阻塞30s,那系统基本就不可用了。

基于上述的问题,java推出了NIO。我们先用一段代码看看NIO的编程

public static void main(String[] args) throws Exception {
        // 打开一个ServerSocketChannel
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.configureBlocking(Boolean.FALSE);
        // 获取ServerSocketChannel绑定的Socket
        ServerSocket socket = socketChannel.socket();
        // 设置ServerSocket监听的端口
        socket.bind(new InetSocketAddress(PORT));
        System.out.println("开始等待客户端连接");
        // 打开一个选择器
        Selector selector = Selector.open();
        // 将ServerSocketChannel注册到选择器上去并监听accept事件
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 这里会发生阻塞,等待就绪的通道
            int select = selector.select();
            // 没有就绪的通道则什么也不做
            if (select == 0) {
                continue;
            }
            // 获取SelectionKeys上已经就绪的通道的集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            // 遍历每一个Key
            while (iterator.hasNext()){
                SelectionKey next = iterator.next();
                if (next.isAcceptable()){
                    ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                    SocketChannel socketChannel1 = channel.accept();
                    socketChannel1.register(selector,SelectionKey.OP_READ);
                }else if (next.isReadable()){
                    readDataFromSocket(next);
                }
                iterator.remove();
            }
        }
    }
    private static ByteBuffer bb = ByteBuffer.allocate(1024);
    private static void readDataFromSocket(SelectionKey next) throws IOException {
        SocketChannel sc = (SocketChannel)next.channel();
        bb.clear();
        while (sc.read(bb)>0){
            bb.flip();//
            //告知在当前位置和限制之间是否有元素
            while (bb.hasRemaining()){
                System.out.println((char) bb.get());
            }
            System.out.println();
            bb.clear();
        }
    }

 java为NIO提供了全新的API,大致有以下三种

  •  缓冲区 Buffer

一个缓冲区对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。从数据结构而言,缓冲区就是一个数组,通常是一个字节数组即ByteBuffer。每一种java基本类型都有对应的缓冲区

技术分享图片

  •  Channel

与socket类和SeverSocket类似。NIO提供了SocketChannelServerSocketChannel ,这两个新增的通道都支持阻塞和非阻塞模式,阻塞模式使用简单,但是性能和可靠性都不好。非阻塞模式则相反。Channel可以自由的设置阻塞对Java来说意义非常重大。试想下之前的BIO网络编程为什么一个连接必须要对应一个线程。由于NIO的channel可以设置非阻塞模式,我们完全可以通过一个线程接受多个socket请求。

有两点需要我们注意:

1.文件通道总是阻塞的,不能设置成非阻塞模式

2.Channel只能往Buffer中写入

  • Selector

选择器的作用是协调管理多个channel,selector定义了4种channel事件,每次channel注册的时候都必须定义好自己关心的是哪一种事件。注册完成后selector会一直阻塞,直到某些事件就绪。

    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;

在了解上述三个api之后,我们再简单分析下上述代码

1.创建ServerSocketChannel
2.设置ServerSocketChannel为非阻塞状态
3.监听端口
4.将ServerSocketChannel 注册到一个Selector
5.等待选择接受就绪事件,一旦接收到 即可做出相应的操作

 

 NIO的阻塞


 

技术分享图片

如上图所示,NIO其实是有阻塞的环节的。那为什么我们仍然称NIO是同步非阻塞IO呢。这里主要涉及到一次完整的io请求是怎么进行读写的。

所有的系统I/O都分为两个阶段:

  等待就绪和操作。举例来说,读函数,分为等待系统可读和真正的读;同理,写函数分为等待网卡可以写和真正的写。等待就绪的阻塞是不使用CPU的,是在“空等”;而真正的读写操作的阻塞是使用CPU的,真正在"干活",而且这个过程非常快,属于memory copy,带宽通常在1GB/s级别以上,可以理解为基本不耗时。

  对于BIO而言,如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据,再阻塞的读到的数据。

  对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

  所以,socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。这部分的阻塞相对于BIO而言,是可以忽略不计的。所以我们可以认为NIO是非阻塞的。

 

以上是关于NIO 简介的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

java之NIO简介

NIO简介

Java-NIO:简介

NIO简介

NIO 简介