浅析 NIO

https://blog.****.net/u013068377/article/details/70312551(BIO NIO AIO 区别)

一   NIO和IO的区别

1.IO是面向流的,NIO是面向缓冲区的。

2 NIO的通道是可以双向的,但是IO中的流只能是单向的。

3.IO的各种流是阻塞的。NIO是非阻塞的。

二 NIO简介

NIO的三大组件  Channel    Buffer  Selector

Channel

国内大多翻译成“通道”。Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client)。下面演示的案例基本上就是围绕这4个类型的Channel进行陈述的。

Buffer

NIO 中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等这里先不进行陈述。

Selector

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

NIO整体框图:

浅析 NIO

三  用法

Buffer

控制buffer状态的三个变量:

• position:跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置

• limit:代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。

• capacity:代表缓冲区的最大容量,一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的。

最初位置(未读取数据):

浅析 NIO

写入一些数据后:

浅析 NIO

调用buffer.flip() 方法(写模式转换成读模式):

浅析 NIO

这个方法把当前的指针位置position设置成了limit,再将当前指针position指向数据的最开始端,我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

为什么要这样呢?比如需要将缓冲区数据取出来解析(get方法),我现在要读了,就不要再写了,所以调用flip()方法,保存现在状态不再写入数据。

        FileInputStream fis = new FileInputStream(resource);
        FileOutputStream fos = new FileOutputStream(destination);
        FileChannel readChannel = fis.getChannel(); // 读文件通道
        FileChannel writeChannel = fos.getChannel(); // 写文件通道
        ByteBuffer buffer = ByteBuffer.allocate(1024); // 创建缓存区,并设置其大小
        while (true) {
            buffer.clear();                                         //清空缓存区域
            int len = readChannel.read(buffer);      // 读入数据
            if (len == -1) {
                break; // 读取完毕
            }
            buffer.flip();                                           //缓存区状态转变
            writeChannel.write(buffer);                // 写入文件
        }
        readChannel.close();
        writeChannel.close();

 

Selector

用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。

                     浅析 NIO

当有读或者写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。
使用NIO中非阻塞IO编写服务器处理程序,有三个步骤
1.向Selector对象注册感兴趣的事件
2.从Selector中获取感兴趣的事件
3.根据不同事件进行相应的处理


               Selector selector = Selector.open();       //创建  Selector
               channel.configureBlocking(false);         //设置channel为非租塞
               SelectionKey key = channel.register(selector, Selectionkey.OP_READ);     //注册到Selector
第二个参数意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:

1. SelectionKey.OP_CONNECT       //连接就绪
2. SelectionKey.OP_ACCEPT       //接收就绪
3. SelectionKey.OP_READ         //读就绪
4. SelectionKey.OP_WRITE        //写就绪

                       浅析 NIO
 

关于 SelectionKey   与  通过Selector选择通道 的更多信息:

参考:https://blog.****.net/u011381576/article/details/79876754

 

 

 

反应堆

1)阻塞IO模型
在老的IO包中,serverSocket和socket都是阻塞式的,因此一旦有大规模的并发行为,而每一个访问都会开启一个新线程。这时会有大规模的线程上下文切换操作(因为都在等待,所以资源全都被已有的线程吃掉了),这时无论是等待的线程还是正在处理的线程,响应率都会下降,并且会影响新的线程。

浅析 NIO

2)NIO
Java NIO是在jdk1.4开始使用的,它既可以说成“新IO”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:
1.由一个专门的线程来处理所有的IO事件,并负责分发。
2.事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3.线程通讯:线程之间通过wait,notify等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

浅析 NIO

 

引用:

https://blog.****.net/u011381576/article/details/79876754

https://blog.****.net/CharJay_Lin/article/details/81810922