一、NIO Selector的定义和作用
Java NIO(New IO)是从Java 1.4版本开始引入的,用于替代原来的Java IO API,NIO提供了一种面向缓冲区的、基于通道的I/O操作方式。NIO中的Selector其实是一个选择器,用于处理多个通道的选择性IO操作。以前,一个线程只能处理一个连接,而Selector提供了一种可以通过一个线程处理多个连接的方式。 在使用NIO编程时,Selector负责监控被注册进它里面的Channel,当Channel中有可以进行IO操作的状态时,Selector将会得到通知,进行相应的处理,而不需要线程一直轮询。因此,NIO Selector解决了Java BIO的瓶颈问题,保证了处理高并发连接时系统性能和可靠性的平衡。
二、NIO Selector的基本使用方法
下面给出一个简单的例子来说明如何使用NIO Selector实现服务端与客户端的简单通信:
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(8080));
serverSocket.configureBlocking(false);
SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("建立链接");
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
String receive = new String(buffer.array());
System.out.println("接收到客户端的信息:" + receive);
}
iterator.remove();
}
}
上述代码中,我们首先创建一个Selector实例并打开一个ServerSocketChannel,然后将ServerSocketChannel注册到Selector上,并且声明该Selector对客户端连接请求(OP_ACCEPT)感兴趣。 最后通过不断地轮询select()方法,来检查是否有连接已经准备好进行I/O操作。在select()方法返回之后,我们通过迭代器获取可操作的SelectionKey集合,然后进行相应操作,例如建立连接或者从通道中读取数据。
三、NIO Selector的优缺点
NIO Selector有以下几个优点:
- 单线程可以处理多个连接,降低了系统开销,提高了系统的伸缩性;
- 为非阻塞I/O提供了一个优良的基础,Selector监听多个Channel的I/O事件的能力使得单个线程可以处理多个并发通道,从而提高了系统的并发处理能力;
- 提供了更高效的事件通知机制。当向Selector注册Channel时,可以指定我们感兴趣的操作类型(Connect、Accept、Read、Write),只有在这些事件发生时,Selector才会返回,否则就一直阻塞。这样,我们就可以用一个线程去管理多个channel,也就是NIO实现高效的处理方式的基础; 但同时,NIO Selector也有以下几个缺点:
- 对于连接的管理较为复杂,且处理细节需要高度注意;
- 编程较为繁琐,需要一定的经验和能力;
- 自己编写网络框架时,使用NIO Selector需要考虑到众多细节问题,涉及到多线程、使用ByteBuffer等方面;