如果调用wakeup()时并没有线程阻塞在select()上,那么,下一次调用select()将立即返回,不会进入阻塞状态。这跟LockSupport.unpark()方法是比较类似的。
Selector.close()
调用close()方法将会关闭Selector,同时也会将关联的SelectionKey失效,但不会关闭Channel。
举个栗子

public class EchoServer {
public static void main(String[] args) throws IOException {
// 创建一个Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 绑定8080端口
serverSocketChannel.bind(new InetSocketAddress(8080));
// 设置为非阻塞模式,本文来源于工从号彤哥读源码
serverSocketChannel.configureBlocking(false);
// 将Channel注册到selector上,并注册Accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞在select上
selector.select();
// 如果使用的是select(timeout)或selectNow()需要判断返回值是否大于0
// 有就绪的Channel
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 遍历selectKeys
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 如果是accept事件
if (selectionKey.isAcceptable()) {
// 强制转换为ServerSocketChannel
ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = ssc.accept();
System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
socketChannel.configureBlocking(false);
// 将SocketChannel注册到Selector上,并注册读事件
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 如果是读取事件
// 强制转换为SocketChannel
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 创建Buffer用于读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将数据读入到buffer中
int length = socketChannel.read(buffer);
if (length > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
// 将数据读入到byte数组中
buffer.get(bytes);
// 换行符会跟着消息一起传过来
String content = new String(bytes, "UTF-8").replace("\r\n", "");
if (content.equalsIgnoreCase("quit")) {
selectionKey.cancel();
socketChannel.close();
} else {
System.out.println("receive msg: " + content);
}
}
}
iterator.remove();
}
}
}
}
总结
今天我们学习了Java NIO核心组件Selector,到这里,NIO的三个最重要的核心组件我们就学习完毕了,说实话,NIO这块最重要的还是思维的问题,时刻记着在NIO中一个线程是可以处理多个连接的。
看着Java原生NIO实现网络编程似乎也没什么困难的吗?那么为什么还要有Netty呢?下一章我们将正式进入Netty的学习之中,我们将在其中寻找答案。
最后,也欢迎来我的工从号彤哥读源码系统地学习源码&架构的知识。
