Java NIO - 选择器
众所周知,Java NIO 支持来自和到通道和缓冲区的多个事务。因此,为了检查一个或多个 NIO 通道,并确定哪些通道已准备好进行数据事务(即读取或写入),Java NIO 提供选择器。
使用选择器,我们可以创建一个线程来了解哪个通道已准备好进行数据写入和读取,并可以处理该特定通道。
我们可以通过调用其静态方法 open() 来获取选择器实例。打开选择器后,我们必须向其注册一个非阻塞模式通道,该通道返回 SelectionKey 的实例。
SelectionKey 基本上是可以使用通道执行的操作的集合,或者我们可以说,我们可以借助选择键了解通道的状态。
选择键代表的主要操作或通道状态是 −
SelectionKey.OP_CONNECT − 准备连接服务器的通道。
SelectionKey.OP_ACCEPT − 准备接受传入连接的通道。
SelectionKey.OP_READ − 准备读取数据的通道。
SelectionKey.OP_WRITE − 准备写入数据的通道。
注册后获得的Selection Key有一些重要的方法,如下所述 −
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(); } } } }