當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Java java.nio.channels.Selector用法及代碼示例


選擇器是一種工具,可幫助您關注一個或多個 NIO 通道並確定它們何時準備好傳輸數據。在Java NIO,選擇器是查看多個的關鍵角色Java NIO 通道實例並找出哪些適合進行閱讀或寫作等活動。

這允許單個線程處理多個通道,從而可以輕鬆管理多個網絡連接。

A Thread uses a Selector to handle 4 Channel's

為什麽選擇器?

使用選擇器可以幫助我們用更少的線程做更多的事情,隻需一個而不是多個。頻繁的線程切換會給操作係統帶來負擔並耗盡內存。

  • 我們使用的線程越少,係統的效率就越高。
  • 值得注意的是,現代操作係統和 CPU 在處理多個任務方麵做得越來越好。
  • 有助於減少隨著時間的推移使用多個線程的資源需求。

選擇器不僅可以讀取數據,還可以監視傳入的網絡連接,並使通過較慢的通道發送數據變得更容易。

如何創建選擇器?

隻需使用 Selector 類的 open 方法即可創建選擇器。此方法利用係統的默認選擇器提供程序來生成新的選擇器

Selector selector = Selector.open();

使用選擇器注冊通道

為了讓選擇器監視任何通道,我們必須向選擇器注冊這些通道。
這是通過調用 SelectableChannel 的 register 方法來完成的。但是,在向選擇器注冊之前,通道必須處於非阻塞模式:

channel.configureBlocking(false);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);

第二個參數定義了一個興趣集,這意味著我們有興趣通過選擇器在受監控的通道中偵聽哪些事件。
您可以收聽四種不同的事件:

  1. 連接:當客戶端嘗試連接到服務器(SelectionKey.OP_CONNECT)。
  2. 接受:當服務器接受來自 a 的連接時客戶端(SelectionKey.OP_ACCEPT)。
  3. 讀:當服務器準備好讀取時頻道(SelectionKey.OP_READ)。
  4. 寫:當服務器準備好寫入時頻道(SelectionKey.OP_WRITE)。

選擇鍵

正如您在上一節中看到的,當我們向選擇器注冊 Channel 時,register() 方法返回 SelectionKey 對象。這個SelectionKey對象包含一些有趣的屬性:

  1. 興趣設定
  2. 準備好的套裝
  3. 這個頻道
  4. 選擇器
  5. 附加對象

興趣集

興趣集指定選擇器應在通道上監視的事件。該興趣集表示為整數值,我們可以如下檢索該信息。

int set = selectionKey.interestOps();
boolean isAccept = set & SelectionKey.OP_ACCEPT;
boolean isConnect = set & SelectionKey.OP_CONNECT;
boolean isRead = set & SelectionKey.OP_READ;
boolean isWrite = set & SelectionKey.OP_WRITE;

最初,我們使用SelectionKey的interestOps方法獲取興趣集。
當我們對這兩個值進行按位與運算時,它會產生一個布爾值,指示事件是否正在被監視。

準備好的套裝

就緒集是通道已準備好的操作集。我們可以像下麵這樣訪問準備好的集合。

int readySet = selectionKey.readyOps();

我們還可以使用下麵四種方法來代替,它們都返回一個布爾值。

  • SelectionKey.isAcceptable();
  • SelectionKey.isConnectable();
  • SelectionKey.isReadable();
  • SelectionKey.isWritable();

這個頻道

我們可以通過selectionKey訪問頻道,如下所示:

Channel  channel  = selectionKey.channel();

選擇器

我們可以從selectionKey對象中獲取selector對象,如下所示:

Selector selector = selectionKey.selector();

附加對象

我們可以將一個對象附加到 SelectionKey。以下是我們如何從 SelectionKey 附加和獲取對象:

selectionKey.attach(object);
Object attachedObject = selectionKey.attachment();

頻道鍵選擇

一旦我們向選擇器注冊了一個或多個通道,我們就可以使用 select() 方法之一。這些方法為我們提供了 “ready” 的通道,用於我們關心的事件(例如連接、接受、讀取或寫入)。

int channels = selector.select();
int channels = selector.select(long timeout);
int channels = selector.selectNow();
  • select() 會阻塞,除非有一個通道準備好接收我們注冊的事件。
  • select(long timeout) 的作用與上麵相同,隻是它會阻塞最大超時毫秒數。
  • selectNow() 根本不阻塞。無論通道準備就緒,它都會立即返回。

一旦我們調用了select()方法及其返回值表明一個或多個通道已就緒,您可以通過選定的鍵集訪問就緒通道,方法是調用selectedKeys()方法如下:

Set<SelectionKey> selectedKeys = selector.selectedKeys();

其他選擇器方法

我們還提到了更多可用的選擇器方法,並提供了正確的說明和用法。

方法

用法

說明

用法

wakeup()

選擇器.wakeup();

wakeup() 方法用於中斷阻塞select() 調用。通常從另一個線程調用它來喚醒 select() 方法。

當需要修改Selector或阻止其阻塞時,可以使用wakeup()喚醒select()調用並使其返回。

close()

選擇器.close();

close()方法用於關閉選擇器。一旦選擇器關閉,它就不能再用於監視通道。

使用完選擇器後關閉它以釋放係統資源非常重要。

實際例子

讓我們探索幾個實際示例來了解 Selector 類的實際應用。

示例 1:簡單的選擇器用法

在此示例中,我們將使用 Java NIO 選擇器來監視單個非阻塞 SocketChannel 的讀取操作。步驟如下:

  1. 創建一個選擇器來有效管理 I/O 就緒監控。
  2. 配置非阻塞SocketChannel用於從遠程服務器讀取數據。
  3. 使用選擇器注冊SocketChannel,指定您感興趣的OP_READ 操作。
  4. 使用選擇器.select()等待至少一個注冊通道準備好讀取。
// Create a Selector
Selector selector = Selector.open();
// Configure and register a non-blocking SocketChannel for read operationsSocketChannel
socketChannel = SocketChannel.open(new InetSocketAddress("example.com", 80));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
// Wait for a ready channelint
readyChannels = selector.select();

示例 2:使用多個通道

在此示例中,我們擴展了 Java NIO 選擇器的使用來管理多個通道,每個通道具有不同的 I/O 操作。步驟如下:

  1. 創建一個選擇器來有效管理 I/O 就緒監控。
  2. 根據您的應用程序需求創建和配置多個通道。
  3. 使用選擇器注冊每個通道,指定您對每個通道感興趣的特定 I/O 操作。
  4. 采用選擇器.select()有效地等待至少一個已注冊的通道準備好執行其指定的操作(例如,讀或寫)。
// Create a Selector
Selector selector = Selector.open();
// Create and configure multiple channels
SocketChannel channel1 = /* Create and configure your channel */;
SocketChannel channel2 = /* Create and configure another channel */;
// Register channels for specific operations
channel1.register(selector, SelectionKey.OP_READ);
channel2.register(selector, SelectionKey.OP_WRITE);
// Wait for ready channelsint ready
Channels = selector.select();

完整示例

為了練習我們在前麵幾節中學到的知識,讓我們看一個完整的client-server示例

  1. 我們將構建一個回顯服務器和一個回顯客戶端。
  2. 在這種設置中,客戶端連接到服務器並開始向其發送消息。
  3. 服務器回顯每個客戶端發送的消息。

當服務器遇到特定的消息,如end,則將其解釋為通信結束,並關閉與客戶端的連接

創建服務器:NIOServer.java

Java


//Java Program to test  
//echo server using NIO Selector 
  
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
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; 
  
//Driver class for NIOServer 
public class NIOServer {     
    
  //Main method 
  public static void main(String[] args) throws IOException { 
      
    // Create a new Selector         
    Selector selector = Selector.open();   
      
    // Create a ServerSocketChannel, bind it, and configure it as non-blocking         
    ServerSocketChannel serverChannel = ServerSocketChannel.open();         
    serverChannel.bind(new InetSocketAddress("localhost", 5454));         
    serverChannel.configureBlocking(false);         
      
    // Register the server socket channel with the Selector for accepting connections         
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);         
    ByteBuffer buffer = ByteBuffer.allocate(256);         
    System.out.println("Server started and listening on port 5454...");         
    while (true) {             
        
      // Select ready channels using the Selector             
      selector.select();             
        
      // Get the set of selected keys             
      Set<SelectionKey> selectedKeys = selector.selectedKeys();             
      Iterator<SelectionKey> keyIterator = selectedKeys.iterator();   
        
      while (keyIterator.hasNext()) {  
          
        SelectionKey key = keyIterator.next();                 
        if (key.isAcceptable()) { 
            
          // Accept a new client connection                     
          ServerSocketChannel server = (ServerSocketChannel) key.channel();  
          SocketChannel clientChannel = server.accept();                 
          clientChannel.configureBlocking(false);                    
            
          // Register the client channel with the Selector for reading   
          clientChannel.register(selector, SelectionKey.OP_READ);                 
        }                 
        if (key.isReadable()) {                    
            
          // Read data from the client                     
          SocketChannel client = (SocketChannel) key.channel();                     
          buffer.clear();                     
          int bytesRead = client.read(buffer);                     
          if (bytesRead == -1) {                         
              
            // Client closed the connection                         
            key.cancel();                         
            client.close();                         
            continue;                     
          }                     
            
          buffer.flip();                     
          String receivedMessage = new String(buffer.array(), 0, bytesRead);  
            
          // Process the received message (e.g., echo it back to the client)  
          System.out.println("Received: " + receivedMessage);               
            
          // Prepare the buffer for writing and echo the received message back to the client  
          buffer.rewind();                     
          client.write(buffer);                 
        }                 
          
        // Remove the processed key from the set         
        keyIterator.remove();      
      }         
    }     
  } 
}

NIO服務器的輸出:
NIO Echo Server console output

解釋

  • 該代碼是 Java NIO 的實現回顯服務器.
  • 它使用一個選擇器以實現高效的 I/O 處理。
  • 偵聽端口 5454、接受連接並回顯收到的數據。
  • 非阻塞 I/O 允許同時處理多個連接。
  • 服務器循環監視注冊通道上的事件。
  • 接受的連接被注冊以供讀取,並且數據被回顯給客戶端。

創建客戶端:NIOClient.java

在此示例中,我們將使用 SocketChannel 創建一個 NIOClient。

Java


//Java Program to create a  
//echo client using NIO Selector 
  
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.SocketChannel; 
  
  
//Driver class for NIOClient 
public class NIOClient {     
    
  //Main method 
  public static void main(String[] args) throws IOException {   
      
    // Create a socket channel and connect to the server      
    SocketChannel clientChannel = SocketChannel.open();      
    clientChannel.connect(new InetSocketAddress("localhost", 5454));   
    ByteBuffer buffer = ByteBuffer.allocate(256);      
    String message = "Hello, NIO Server!";  
      
    // Message to send to the server        
    buffer.clear();        
    buffer.put(message.getBytes());        
    buffer.flip();        
      
    // Send the message to the server        
    while (buffer.hasRemaining()) {            
      clientChannel.write(buffer);         
    }         
    buffer.clear();        
      
    // Read the server's response        
    clientChannel.read(buffer);         
    buffer.flip();         
      
    // Convert the response to a String and print it         
    String response = new String(buffer.array(), 0, buffer.limit());         
    System.out.println("Server Response: " + response);         
    clientChannel.close();     
  } 
}

NIO客戶端的輸出:

Output of NIO Client

上述程序的解釋:

  • 創建一個SocketChannel並通過端口 5454 連接到本地主機上的服務器。
  • 準備一條消息(“Hello,NIO Server!”)並將其發送到服務器。
  • 將服務器的響應讀入緩衝區並將其轉換為字符串。
  • 打印服務器的響應。
  • 通信後關閉客戶端通道。

結論

java.nio.channels.Selector類是掌握 Java 中非阻塞 I/O 的重要組件。通過了解如何創建選擇器, 與SelectionKey對象,並利用select()方法,我們可以顯著提高 Java 應用程序的效率和響應能力。



相關用法


注:本文由純淨天空篩選整理自lavkumar5大神的英文原創作品 java.nio.channels.Selector Class in Java。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。