Java NIO - 選擇器



眾所周知,Java NIO 支援與通道和緩衝區之間進行多重事務處理。因此,為了檢查一個或多個 NIO 通道,並確定哪些通道已準備好進行資料事務處理(即讀取或寫入),Java NIO 提供了選擇器。

使用選擇器,我們可以建立一個執行緒來了解哪些通道已準備好進行資料寫入和讀取,並可以處理該特定通道。

我們可以透過呼叫其靜態方法 `open()` 來獲取選擇器例項。開啟選擇器後,我們必須向其註冊一個非阻塞模式通道,這將返回一個 SelectionKey 例項。

SelectionKey 本質上是可以在通道上執行的操作集合,或者可以說我們可以藉助選擇鍵瞭解通道的狀態。

選擇鍵表示的主要操作或通道狀態如下:

  • SelectionKey.OP_CONNECT - 準備好連線到伺服器的通道。

  • SelectionKey.OP_ACCEPT - 準備好接受傳入連線的通道。

  • SelectionKey.OP_READ - 準備好讀取資料的通道。

  • SelectionKey.OP_WRITE - 準備好寫入資料的通道。

註冊後獲得的選擇鍵有一些重要的方法,如下所示:

  • attach() - 此方法用於將物件附加到鍵。將物件附加到通道的主要目的是識別同一個通道。

  • attachment() - 此方法用於從通道中保留附加的物件。

  • channel() - 此方法用於獲取為其建立特定鍵的通道。

  • selector() - 此方法用於獲取為其建立特定鍵的選擇器。

  • isValid() - 此方法返回鍵是否有效。

  • isReadable() - 此方法表明鍵的通道是否準備好讀取。

  • isWritable() - 此方法表明鍵的通道是否準備好寫入。

  • isAcceptable() - 此方法表明鍵的通道是否準備好接受傳入連線。

  • isConnectable() - 此方法測試此鍵的通道是否已完成或未能完成其套接字連線操作。

  • isAcceptable() - 此方法測試此鍵的通道是否準備好接受新的套接字連線。

  • interestOps() - 此方法檢索此鍵的興趣集。

  • readyOps() - 此方法檢索就緒集,即通道已準備好執行的操作集。

我們可以透過呼叫其靜態方法 `select()` 從選擇器中選擇一個通道。選擇器的 select 方法是過載的,如下所示:

  • select() - 此方法阻塞當前執行緒,直到至少有一個通道已準備好處理其註冊的事件。

  • select(long timeout) - 此方法與 select() 執行相同的操作,但它最多阻塞執行緒 timeout 毫秒(引數)。

  • selectNow() - 此方法根本不會阻塞。它立即返回任何已準備好的通道。

此外,為了離開呼叫 select 方法的阻塞執行緒,可以在選擇器例項中呼叫 `wakeup()` 方法,之後在 select() 中等待的執行緒將立即返回。

最後,我們可以透過呼叫 `close()` 方法來關閉選擇器,該方法還會使與此選擇器註冊的所有 SelectionKey 例項失效,以及關閉選擇器。

示例

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SelectorDemo {
   public static void main(String[] args) throws IOException {
      String demo_text = "This is a demo String";	
      Selector selector = Selector.open();
      ServerSocketChannel serverSocket = ServerSocketChannel.open();
      serverSocket.bind(new InetSocketAddress("localhost", 5454));
      serverSocket.configureBlocking(false);
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      ByteBuffer buffer = ByteBuffer.allocate(256);
      while (true) {
         selector.select();
         Set<SelectionKey> selectedKeys = selector.selectedKeys();
         Iterator<SelectionKey> iter = selectedKeys.iterator();
         while (iter.hasNext()) {
            SelectionKey key = iter.next();
            int interestOps = key.interestOps();
            System.out.println(interestOps);
            if (key.isAcceptable()) {
               SocketChannel client = serverSocket.accept();
               client.configureBlocking(false);
               client.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
               SocketChannel client = (SocketChannel) key.channel();
               client.read(buffer);
               if (new String(buffer.array()).trim().equals(demo_text)) {
                  client.close();
                  System.out.println("Not accepting client messages anymore");
               }
               buffer.flip();
               client.write(buffer);
               buffer.clear();
            }
            iter.remove();
         }
      }
   }
}
廣告
© . All rights reserved.