Java NIO選擇器


在Java NIO中,選擇器(Selector)是可選擇通道的多路複用器,可用作可以進入非阻塞模式的特殊型別的通道。它可以檢查一個或多個NIO通道,並確定哪個通道準備好了可以進行通訊,即讀取或寫入。

選擇器的用途是什麼?

選擇器(Selector)用於使用單個執行緒處理多個通道。 因此,它需要較少的執行緒來處理這些通道。 執行緒之間的切換對於作業系統來說是昂貴的。 因此,使用它可以提高系統效率。

下面來看看使用選擇器來處理3個通道的執行緒的示意圖:

下面是聚集原理的簡單說明:

建立選擇器

可以通過呼叫Selector.open()方法建立一個選擇器,如下程式碼所示:

Selector selector = Selector.open();

開啟伺服器通訊端通道

下面來看看開啟伺服器通訊端通道的例子:

ServerSocketChannel serverSocket = ServerSocketChannel.open();  
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 8099);  
serverSocket.bind(hostAddress);

使用選擇器選擇通道

在使用選擇器註冊一個或多個通道時,可以呼叫select()方法之一。 該方法返回一個準備好進行要執行事件的通道,即:連線,讀取,寫入或接受。

可用於選擇通道的各種select()方法有:

  • int select():由select()方法返回的整數值通知有多少個通道準備好進行通訊。
  • int select(long TS):方法與select()相同,除了阻塞最大TS(毫秒)時間的輸出。
  • int selectNow():它不阻止輸出並立即返回任何準備好的通道。

  • selectedKeys() - 當呼叫了任何一個select()方法後,它將返回一個值,表示一個或多個通道準備就緒,那麼我們可以通過使用選擇的鍵集合來存取就緒通道,通過呼叫選擇器selectedkeys()方法如下:

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

可以疊代所選的鍵集合來存取準備好的通道,如下所示:

Set<SelectionKey> selectedKeys = selector.selectedKeys();  
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();  
while(keyIterator.hasNext()) {    
    SelectionKey key = keyIterator.next();  
    if(key.isConnectable()) {  
        // The connection was established with a remote server.  
    } else if (key.isAcceptable()) {  
        // The connection was accepted by a ServerSocketChannel.  
    } else if (key.isWritable()) {  
        //  The channel is ready for writing  
    } else if (key.isReadable()) {  
        // The channel is ready for reading  
    }  
    keyIterator.remove();  
}

上述回圈疊代所選擇的鍵集合中的鍵,以確定使用所選通道執行的操作。

完整的選擇迴圈示意圖如下所示:

基本選擇器範例

主程式:

package com.yiibai;

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

public class SelectorExample {
    public static void main(String[] args) throws IOException {
        // Get the selector
        Selector selector = Selector.open();
        System.out.println("Selector is open for making connection: " + selector.isOpen());
        // Get the server socket channel and register using selector
        ServerSocketChannel SS = ServerSocketChannel.open();
        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 8080);
        SS.bind(hostAddress);
        SS.configureBlocking(false);
        int ops = SS.validOps();
        SelectionKey selectKy = SS.register(selector, ops, null);
        for (;;) {
            System.out.println("Waiting for the select operation...");
            int noOfKeys = selector.select();
            System.out.println("The Number of selected keys are: " + noOfKeys);
            Set selectedKeys = selector.selectedKeys();
            Iterator itr = selectedKeys.iterator();
            while (itr.hasNext()) {
                SelectionKey ky = (SelectionKey) itr.next();
                if (ky.isAcceptable()) {
                    // The new client connection is accepted
                    SocketChannel client = SS.accept();
                    client.configureBlocking(false);
                    // The new connection is added to a selector
                    client.register(selector, SelectionKey.OP_READ);
                    System.out.println("The new connection is accepted from the client: " + client);
                } else if (ky.isReadable()) {
                    // Data is read from the client
                    SocketChannel client = (SocketChannel) ky.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    client.read(buffer);
                    String output = new String(buffer.array()).trim();
                    System.out.println("Message read from client: " + output);
                    if (output.equals("Bye Bye")) {
                        client.close();
                        System.out.println("The Client messages are complete; close the session.");
                    }
                }
                itr.remove();
            } // end of while loop
        } // end of for loop
    }
}

用戶端程式:



執行上面範例程式,得到以下結果 -

主程式的輸出是:

Selector is open for making connection: true
Waiting for the select operation...
The Number of selected keys are: 1
The new connection is accepted from the client: java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:53823]
Waiting for the select operation...
The Number of selected keys are: 1
Message read from client: Time goes fast.
Waiting for the select operation...
The Number of selected keys are: 1
Message read from client: What next?
Waiting for the select operation...
The Number of selected keys are: 1
Message read from client: Bye Bye
The Client messages are complete; close the session.

用戶端程式的輸出是:

The Client is sending messages to server...
Time goes fast.
What next?
Bye Bye