最后更新时间:2014-06-23
一个Selector是一个Java NIO的组件,他可以检测一个或者多个NIO的通道,并且决定哪个通道准备好某些事件,例如读或者写。这种方式一个单独的线程就可以管理多个通道,以及多个网络连接。
为什么使用Selector
使用只是单线程去管理多个通道的优点就是你需要更少的线程去处理这些通道。实际上,你可以只是使用一个线程去处理所有的通道。线程切换对于操作系统是昂贵的,并且每一个线程在操作系统中也会消耗一些资源(内存)。因此,使用越少的线程越好。
但是请记住,现在的操作系统和CPU在多任务处理中变得越来越好,以至于多线程的负荷随着时间的推移变得很小了。实际上,如果一个CPU是多核,你可能会浪费CPU的能力在不是多任务的情况下。不管怎样,那个设计的主题属于不同的主题。它足够在这里说了,使用Selector,你可以用单线程处理多个channels。
这里有一个在单线程的情况下去处理三个通道的图解:
创建一个Selector
通过调用Selector.open()方法可以创建一个Selector,像下面这样:
Selector selector = Selector.open();
为了跟随Selector使用一个Channel,你必须用Selector注册一个通道。这个可以使用SelectableChannel.register()方法去实现,像下面这样:
channel.configureBlocking(false); SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
注意register()方法的第二个参数。这是一个“兴趣集合”,意味着你对Channel中的什么事件监听感兴趣,通过这个Selector。这里有你可以监听的四种不同的事件类型:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT; boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
int readySet = selectionKey.readyOps();
selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();
Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);通过一个Selector选择通道
Set<SelectionKey> selectedKeys = selector.selectedKeys();当你用Selector注册一个通道的时候,这个Channel.register()方法返回一个SelectionKey对象。这一项显示了通道注册在那个selector上。你可以通过selectedKeySet()方法访问这些项。来自于SelectionKey。
Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } keyIterator.remove(); }
Selector selector = Selector.open(); channel.configureBlocking(false); SelectionKey key = channel.register(selector, SelectionKey.OP_READ); while(true) { int readyChannels = selector.select(); if(readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } keyIterator.remove(); } }
翻译地址:http://tutorials.jenkov.com/java-nio/selectors.html