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入門

相關文章
相關標籤/搜索