Java Nio 简明教程
Java NIO - Selector
如我们所知,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() - 此方法测试密钥的通道是否已完成或失败完成其 socket 连接操作。
-
isAcceptable() - 此方法测试密钥的通道是否已准备好接受新的 socket 连接。
-
interestOps() - 此方法检索此密钥的兴趣集。
-
readyOps() - 此方法检索就绪集,它是通道已准备好的操作的集合。
通过调用其静态方法 select() ,我们可以从选择器中选择通道。选择器的选择方法被重载为 -
-
select() -此方法阻塞当前线程,直到至少一个通道为其注册的事件准备好。
-
select(long timeout) - 此方法与 select() 相同,但它最多阻塞线程 timeout 毫秒(参数)。
-
selectNow() - 此方法根本不会阻塞。它立即返回任何就绪的通道。
此外,为了离开调用选择方法的阻塞线程,可以在选择器实例中调用 wakeup() 方法,然后在 select() 中等待的线程将立即返回。
最后,我们可以通过调用 close() 方法关闭选择器,该方法还会使与此选择器注册的所有 SelectionKey 实例无效,同时关闭选择器。
Example
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();
}
}
}
}