Java NIO選擇器


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

選擇器的用途是什麼?

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

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

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

Java NIO選擇器

創建選擇器

可以通過調用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();  
}

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

完整的選擇循環示意圖如下所示:

Java NIO選擇器

基本選擇器示例

主程序:

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