NIO学习笔记

1、NIO的核心部分

  1. 通道(channel):用于读操做和写操做,主要负责数据的“运输”。至关于传统IO中的stream,不过stream是单向的。
  2. 缓冲区(buffer):一个容器,用于存储数据。
  3. 选择器(selector):核心类,可以检测多个注册的通道上是否有事件发生,所以一个线程能够管理多个通道。
    图片描述
    图片描述

2、为何使用NIO

使用NIO主要就是为了提升IO的速度html

  1. 传统IO是基于字节流字符进行操做的,而NIO是基于通道(channel)缓冲区(buffer)进行操做的,这样一来数据的偏移操做很是方便。
  2. 传统IO是阻塞的,若是发出IO请求后数据没有就绪,会处于阻塞状态,而NIO经过通道操做数据,所以一个通道无数据可读,能够切换到另外一个通道。
  3. NIO有选择器,所以单线程能够操做多个通道。

3、简单的NIO读和写例子

  1. 从文件中读取
    与传统IO不一样的是,使用NIO从文件中读取主要分为三步:
    (1)从FileInputStream中获取channel;java

    FileInputStream fin = new FileInputStream( "readandshow.txt" );
    FileChannel fc = fin.getChannel();

    (2)建立buffer;数组

    ByteBuffer buffer = ByteBuffer.allocate( 1024 );

    (3)将数据从channel读到buffer中。服务器

    fc.read( buffer );
  2. 写入文件
    相似的,利用NIO写入文件也分为三步:
    (1)从FileInputStream中获取channel;网络

    FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
       FileChannel fc = fout.getChannel();

    (2)建立一个缓冲区并在其中放入一些数据;函数

    ByteBuffer buffer = ByteBuffer.allocate( 1024 );
       for (int i=0; i<message.length; ++i) {
           buffer.put( message[i] );  //从message数组中取出放入buffer
       }
       buffer.flip();    //将buffer由写模式切换为读模式

    (3)将数据从buffer写到通道中。学习

    fc.write( buffer );

4、buffer内部细节

  1. flip()函数源码
public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

这里能够注意到limit,position和mark三个变量
(1) capacity:代表能够储存在缓冲区中的最大数据容量。
(2) position:下一个可插入的位置(写buffer时)或者下一个可读的位置(读buffer时)。
(3) limit:最多能写多少数据(写buffer时,至关于capacity),能够读多少数据(在从通道读入缓冲区时)。
(4) mark:标记,记录当前position的位置,能够经过reset()恢复到mark的位置。大数据

5、选择器

  1. selector的建立

    Selector selector = Selector.open();this

  2. 由于选择器是用于管理通道的,所以须要将通道注册到相应的选择器上
    channel.configureBlocking(false); //使用selector必须保证channel是非阻塞的模式
    SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

    注意register的第二个参数表示对选择器对什么事件感兴趣。
    返回值记录了包括channel,buffer,interest集合和ready集合等。spa

  3. 经过selector选择通道
    可使用select()函数进行选择,select()方法返回的int值表示有多少通道已经就绪。
  4. selectedKeys()函数
    一旦调用了select()方法,而且返回值代表有一个或更多个通道就绪了,而后能够经过调用selector的selectedKeys()方法,访问“已选择键集(selected key set)”中的就绪通道。这里的selectorKey的就是以前注册到该selector的通道。
  5. 示例程序

    Selector selector = Selector.open();   //开启选择器
    channel.configureBlocking(false);      //非阻塞模式
    SelectionKey key = channel.register(selector, SelectionKey.OP_READ);//注册
    while(true) {
      int readyChannels = selector.select();   //选择通道
      if(readyChannels == 0) continue;
      Set selectedKeys = selector.selectedKeys();   //获取通道
      Iterator keyIterator = selectedKeys.iterator();
      while(keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        if(key.isAcceptable()) {
            // a connection was accepted by a ServerSocketChannel.
        } else if (key.isConnectable()) {
            // a connection was established with a remote server.
        } else if (key.isReadable()) {
            // a channel is ready for reading
        } else if (key.isWritable()) {
            // a channel is ready for writing
        }
        keyIterator.remove();   //须要本身移除处理完的通道
      }
    }

6、NIO和IO的使用场景

NIO具有必定的优势,但并非说传统IO就一无可取

(1)在处理数据上,若是遇到逐行处理的状况,如:

Name: Anna
    Age: 25
    Email: anna@mailserver.com
    Phone: 1234567890

传统的IO能够这样写:

BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

但若是使用NIO:

ByteBuffer buffer = ByteBuffer.allocate(48);
    int bytesRead = inChannel.read(buffer);

你就不知道缓冲区内是否恰好是一行数据,这样处理起来会比较麻烦。

(2)若是须要管理同时打开的成千上万个链接,这些链接每次只是发送少许的数据,例如聊天服务器,实现NIO的服务器多是一个优点。
若是你须要维持许多打开的链接到其余计算机上,如P2P网络中,使用一个单独的线程来管理你全部出站链接,多是一个优点。
但若是你有少许的链接使用很是高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能很是契合。

学习参考:http://ifeve.com/java-nio-all/ Java NIO 系列教程
https://www.ibm.com/developer... NIO入门

相关文章
相关标签/搜索