1. 程式人生 > 程式設計 >深入瞭解java NIO之Selector(選擇器)

深入瞭解java NIO之Selector(選擇器)

這一節我們將探索選擇器(selectors)。選擇器提供選擇執行已經就緒的任務的能力,這使得多元 I/O 成為可能。就像在第一章中描述的那樣,就緒選擇和多元執行使得單執行緒能夠有效率地同時管理多個 I/O 通道(channels)。C/C++程式碼的工具箱中,許多年前就已經有 select()和 poll()這兩個POSIX(可移植性作業系統介面)系統呼叫可供使用了。許過作業系統也提供相似的功能,但對Java 程式設計師來說,就緒選擇功能直到 JDK 1.4 才成為可行的方案。

下面我們來使用選擇器:

通過 Selector.open()方法,我們可以建立一個選擇器:

Selector selector = Selector.open();

將 Channel 註冊到選擇器中:

channel.configureBlocking(false);

SelectionKey key = channel.register(selector,SelectionKey.OP_READ);

注意,如果一個 Channel 要註冊到 Selector 中,那麼這個 Channel 必須是非阻塞的,即channel.configureBlocking(false);因為 Channel 必須要是非阻塞的,因此 FileChannel 不能夠使用選擇器,因為 FileChannel 都是阻塞的.

注意到,在使用 Channel.register()方法時,第二個引數指定了我們對 Channel 的什麼型別的事件感興趣,這些事件有:

  • Connect,即連線事件(TCP 連線),對應於SelectionKey.OP_CONNECT
  • Accept,即確認事件,對應於SelectionKey.OP_ACCEPT
  • Read,即讀事件,對應於SelectionKey.OP_READ,表示 buffer 可讀.
  • Write,即寫事件,對應於SelectionKey.OP_WRITE,表示 buffer 可寫.

一個 Channel發出一個事件也可以稱為 對於某個事件,Channel 準備好了. 因此一個 Channel 成功連線到了另一個伺服器也可以被稱為 connect ready.

我們可以使用或運算|來組合多個事件,例如:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

注意,一個 Channel 僅僅可以被註冊到一個 Selector 一次,如果將 Channel 註冊到 Selector 多次,那麼其實就是相當於更新 SelectionKey 的 interest set. 例如:

channel.register(selector,SelectionKey.OP_READ);
channel.register(selector,SelectionKey.OP_READ | SelectionKey.OP_WRITE);

上面的 channel 註冊到同一個 Selector 兩次了,那麼第二次的註冊其實就是相當於更新這個 Channel 的 interest set 為 SelectionKey.OP_READ | SelectionKey.OP_WRITE.

但是Java NIO的selector允許一個單一執行緒監聽多個channel輸入。我們可以註冊多個channel到selector上,然後然後用一個執行緒來挑出一個處於可讀或者可寫狀態的channel。selector機制使得單執行緒管理多個channel變得容易。

下面我們寫一個完整的例子,看一下Selector的用法:

//建立選擇器
Selector selector = Selector.open();
channel.configureBlocking(false);
//註冊通道
SelectionKey key = channel.register(selector,SelectionKey.OP_READ);
while(true) {
  //檢視selector中的key是否準備好
  int readyChannels = selector.select();
  //小於0超時,等於0沒準備好,大於0已經準備完畢
  if(readyChannels == 0) continue;
  //獲取選擇器中的key
  Set<SelectionKey> selectedKeys = selector.selectedKeys();
  Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
  while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    //遍歷已選擇鍵集中的每個鍵,並檢測各個鍵所對應的通道的就緒事件
    if(key.isAcceptable()) {
      // 連線已經被ServerSocketChannel所接受
    } else if (key.isConnectable()) {
      // 連線已經被遠端終止.
    } else if (key.isReadable()) {
      // 通道已經準備好讀資料
    } else if (key.isWritable()) {
      // 通道已經準備好寫資料
    }
    keyIterator.remove();
  }
}

選擇器的使用還有很多的細節,我們應該多檢視api文件瞭解各個方法的用法。下一節我們做一個綜合練習,總結一下NIO的使用。

以上就是深入瞭解java NIO之Selector(選擇器)的詳細內容,更多關於java nio Selector(選擇器)的資料請關注我們其它相關文章!