一、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有以下几个优点:
1、单线程可以处理多个连接,降低了系统开销,提高了系统的伸缩性;
2、为非阻塞I/O提供了一个优良的基础,Selector监听多个Channel的I/O事件的能力使得单个线程可以处理多个并发通道,从而提高了系统的并发处理能力;
3、提供了更高效的事件通知机制。当向Selector注册Channel时,可以指定我们感兴趣的操作类型(Connect、Accept、Read、Write),只有在这些事件发生时,Selector才会返回,否则就一直阻塞。这样,我们就可以用一个线程去管理多个channel,也就是NIO实现高效的处理方式的基础;
但同时,NIO Selector也有以下几个缺点:
1、对于连接的管理较为复杂,且处理细节需要高度注意;
2、编程较为繁琐,需要一定的经验和能力;
3、自己编写网络框架时,使用NIO Selector需要考虑到众多细节问题,涉及到多线程、使用ByteBuffer等方面;