谈到Selector的具体使用,那么就要结合BIO,NIO的知识讲解,Selector使用在非阻塞模式NIO场景下,学习NIO之前先要了解BIO原理,下面我们一步步讲解
一.BIO(Blocking-IO)-阻塞IO模式
先来看一下BIO下服务端的代码
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(true);
List<SocketChannel>list = new ArrayList<>();
ByteBuffer buffer = ByteBuffer.allocate(16);
while(true){
SocketChannel socketChannel = ssc.accept();
list.add(socketChannel);
for (SocketChannel sc : list) {
sc.read(buffer);
buffer.flip();
System.out.println(Charset.defaultCharset().decode(buffer));
}
}
}
阻塞的体现在于
1.当没有连接请求的时候,线程会阻塞在accept()方法等待,直到有连接过来
SocketChannel socketChannel = ssc.accept();
2.当没有SocketChannel 通道没有消息的时候,线程会阻塞在read()方法,直到通道中有消息,线程开始读取
sc.read(buffer);
也即所有通道(ServerSocketChannel和SocketChannel)的核心方法都是阻塞模式的
二.NIO(Non Blocking-IO)-非阻塞IO模式
我们可以通过设置参数修改通道为非阻塞模式,通过configureBlocking(false),把参数设置为false,false对应非阻塞,true对应阻塞,默认为true
1.对于服务端的ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
2.对于客户端的SocketChannel
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
非阻塞的体现:
1.当服务端没有收到连接请求,线程不会一直阻塞在accpet()方法等待,而是继续向下执行代码流程
1.同理当通道没有消息处理时,线程也不会阻塞在read()方法等待,而是继续向下执行代码流程
三.Selector基于NIO的使用
Selector作用就是把所有的channel通道注册进去统一管理,然后调用Selector.select()监听通道是否有事件发生,如果没有就阻塞,有就通过selector.selectedKeys()获取所有事件的集合,遍历集合去处理这些通道的事件,请你尝试理解下面的服务端代码,注释很详细
public static void main(String[] args) throws Exception{
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
SelectionKey ssck = ssc.register(selector, 0, null);
ssck.interestOps(SelectionKey.OP_ACCEPT);
ByteBuffer buf = ByteBuffer.allocate(16);
while(true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
SocketChannel sc = (SocketChannel) key.channel();
sc.configureBlocking(false);
SelectionKey sck = sc.register(selector, 0, null);
sck.interestOps(SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
sc.read(buf);
buf.flip();
System.out.println(Charset.defaultCharset().decode(buf));
buf.clear();
}
}catch (Exception e){
e.printStackTrace();
key.cancel();
}
}
}
}
注意:
1.SelectionKey 事件要么被执行,要么被取消(调用key.cancel()方法),不然下一次调用Selector.select()旧的事件SelectionKey 会被认为没有处理,认为有事件要处理,就算没有新事件也不会阻塞
2.每次执行完一个事件SelectionKey,需要从selectedKeys()集合中删除这个key,因为集合不会自动移除这个key,需要手动调用remove()方法,不然下次湖北进行SelectionKey集合的遍历时,旧的key还会被重复执行.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)