-
源码翻译
package java.nio.channels;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
/**
* 【1】可选择信道对象的多路复用器。
* 可以通过调用此类的 #open 方法来创建。
*
* 【2】选择器维护三组选择键集合:
* key set,包含表示此选择器当前频道注册的键。由 #keys 方法返回。
* selected-key set,检测到每个键下已经准备就绪的通道,可以选择其中一种键下的通道进行操作。由 #selectedkeys 方法返回。
* Cancelled key set,是一组已取消但其频道尚未取消注册的密钥。无法直接访问此集合。取消的密钥集始终是密钥集的子集。
* All three sets are empty in a newly-created selector.
*
* 【3】#register方法将 channel的 key添加到 selector 中的 key set。在选择操作期间,取消的键将从键集中删除。key set本身不可直接修改。
*
* 【4】#cancel 取消一个键时,它会被添加到 cancelled-key set 中。
*
* 【5】通过迭代器 Iterator #remove(),从 selected-key set 中移除已经操作过的 key。
*/
public abstract class Selector implements Closeable {
protected Selector() { }
/**
* Opens a selector.
*/
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
/**
* Tells whether or not this selector is open.
*/
public abstract boolean isOpen();
/**
* Returns the provider that created this channel.
*/
public abstract SelectorProvider provider();
/**
* Returns this selector's key set.
*
* <p> The key set is not directly modifiable. A key is removed only after
* it has been cancelled and its channel has been deregistered. Any
* attempt to modify the key set will cause an {@link
* UnsupportedOperationException} to be thrown.
*/
public abstract Set<SelectionKey> keys();
/**
* Returns this selector's selected-key set.
*
* <p> Keys may be removed from, but not directly added to, the
* selected-key set. Any attempt to add an object to the key set will
* cause an {@link UnsupportedOperationException} to be thrown.
*/
public abstract Set<SelectionKey> selectedKeys();
/**
* Selects a set of keys whose corresponding channels are ready for I/O
* operations.
*/
public abstract int selectNow() throws IOException;
/**
* Selects a set of keys whose corresponding channels are ready for I/O
* operations.
*/
public abstract int select(long timeout)
throws IOException;
/**
* Selects a set of keys whose corresponding channels are ready for I/O
* operations.
*/
public abstract int select() throws IOException;
/**
* 使尚未返回的第一个选择操作立即返回。
* 如果当前在调用 #select 或. #select(long),则该调用将立即返回。
* 如果当前没有正在进行的选择操作,则这些方法之一的下一次调用将立即返回,除非同时调用 #selectnow()方法。
* 在任何情况下,该调用返回的值都可能是非零的。
*. 除非同时再次调用此方法,否则 #select 或 #select(long)的后续调用将像往常一样阻塞。
* 在两个连续的选择操作之间多次调用此方法与只调用一次具有相同的效果。
*/
public abstract Selector wakeup();
/**
* Closes this selector.
*/
public abstract void close() throws IOException;
}
-
示例模板
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress("localhost", 8080));
Selector selector = Selector.open();
serverChannel.configureBlocking(false);//设置为非阻塞,才可配合 Selector 使用
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
//serverChannel.register(selector, SelectionKey.OP_ACCEPT|SelectionKey.OP_WRITE);
while(true) {
int readyNum = selector.select();
if (readyNum == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable()) {
// 接受连接
} else if (key.isReadable()) {
// 通道可读
} else if (key.isWritable()) {
// 通道可写
}
it.remove();
}
}
}
-
详解
创建 Selector —> #open()
Selector selector = Selector.open();
注册 Channel 到 Seletor
serverChannel.configureBlocking(false);//设置为非阻塞,才可配合 Selector 使用 serverChannel.register(selector, SelectionKey.OP_ACCEPT); //serverChannel.register(selector, SelectionKey.OP_ACCEPT|SelectionKey.OP_WRITE);
- 必须是非阻塞 Channel 才能注册配合 Selector 使用,所以 FileChannel 不能注册到 Selecrot 上。
- #register() 可注册多个事件,即第二个参数:感兴趣事件是个集合, 可用 | 分隔。
SelectionKey.OP_ACCEPT 接受新连接事件,仅适用于服务端 SelectionKey.OP_CONNECT 连接完成事件( TCP 连接 ),仅适用于客户端 SelectionKey.OP_READ 读事件(可读),适用于两端 SelectionKey.OP_WRITE 写事件(可写),适用于两端
#select() —> #selectedKeys()
当有新增就绪的 Channel ,需要先调用 select 方法,才会添加到“已选择键集( selected key set )”中。否则,直接调用
#selectedKeys()
方法,是无法获得它们对应的 SelectionKey 。
#remove()
最后要将已处理过的事件删除,不然,此事件还存在与 selectedKeys 中,下次调用 #select() 还存在。