Java NIO之Selector多路复用器

Java NIO之Selector多路复用器,第1张

文章目录 回顾一下一、Selector 是什么?二、代码示例1.打开一个ServerSocketChannel2.选择select3.处理事件集合3.NIO 编程步骤总结 小结


回顾一下

之前总结过NIO组件之一 Channel,我们可以通过它来与客户端建立连接,并且设置为非阻塞模式,这样虽然解决了使用BIO可能内存爆掉的问题,但是当客户端只是建立连接,没有数据收发时,如果去轮询每个客户端,可能会造成性能的浪费,这样的客户端占用越多,很有可能导致服务端cpu会飙升。
那我们如何优化这一问题呢?聪明的同学就会想到,让channel只遍历那些有数据收发的客户端不就行了吗?对,NIO另一大重要组件Selector多路复用器由此出现。


一、Selector 是什么?

我们先来看一下Selector处理Channe图示:

Selector 一般被称为选择器 ,也可以翻译为多路复用器 。它是 Java NIO 核心组件中的一个,可以同时监听多个服务器端口,用于检查一个或多个 NIO Channel(通道)的状态是否处于可读、可写。由此可以实现单线程管理多个 channels,也就是可以管理多个网络连接。

二、代码示例 1.打开一个ServerSocketChannel

让其绑定9090端口,设置阻塞方式为非阻塞。
创建一个Selector对象,并且将ServerSocketChannel注册到Selector上,并且监听连接事件。
这里的SelectionKey.OP_ACCEPT是一个IO事件,表示服务器监听到了客户连接,那么服务器可以接收这个连接了,还有一些其他事件:

读就绪: SelectionKey.OP_READ写就绪: SelectionKey.OP_WRITE连接就绪: SelectionKey.OP_CONNECT(表示客户与服务器的连接已经建立成功)
	public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(9090));
        serverSocketChannel.configureBlocking(false);
        //打开Selector处理Channel
        Selector selector = Selector.open();
        //把ServerSocketChannel注册到Selector上,并且selector监听accept连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
2.选择select

selector.select()使第一个尚未返回的选择 *** 作立即返回,如果当前没有事件发生,则会阻塞。其源码是在linux系统上使用epoll系统调用来实现的。所以,Java语言的NIO组件所使用的就是IO多路复用模型。本质上select/epoll系统调用是阻塞式的,这也是为什么他会阻塞等待的原因。

		while(true){
            //它会阻塞等待需要处理的事件发生
            selector.select();

            //获取selector感知到的事件集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
3.处理事件集合

对应上面的四个事件,分别有isAcceptable(),isReadable(),isWriteable()和isConnectable(),事件发生即为true,然后创建SocketChannel,就可执行具体业务 *** 作了,下面示例为通过ByteBuffer进行读数据。

	//遍历SelectionKey,对事件进行处理
    while(iterator.hasNext()){

        SelectionKey key = iterator.next();
        //如果是OP_ACCEPT连接事件。则进行连接获取和事件注册
        if(key.isAcceptable()){
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel socketChannel = server.accept();
            socketChannel.configureBlocking(false);
            //注册读事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        }else if(key.isReadable()){
            //如果是OP_READ读事件。则进行读取
            SocketChannel server = (SocketChannel) key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(32);
            int read = server.read(byteBuffer);

            if(read > 0){
                System.out.println("接收到消息:" + new String( byteBuffer.array() ));

            } else if(read == -1){//如果客户端断开连接,关闭socket
                iterator.remove();
                server.close();
            }

            //从事件集合中删除本次处理的key,防止下次重复处理
            iterator.remove();
        }
    }
3.NIO 编程步骤总结

先创建 ServerSocketChannel 通道,然后创建 Selector 选择器,并绑定监听端口;设置Channel 通道是非阻塞模式,把 Channel 注册到 Socketor 选择器上,监听连接事件;调用Selector 的 select 方法(循环调用),监测通道的就绪状况,然后调用 selectKeys 方法获取就绪 channel 集合;遍历就绪 channel 集合,判断就绪事件类型,实现具体的业务 *** 作最后根据业务,决定是否需要再次注册监听事件,重复执行上面 *** 作。


小结

高性能的Java通信离不开Java NIO组件,现在主流的技术框架或中间件服务器都使用了Java NIO组件,譬如Tomcat、Jetty、Netty。不管是面试还是实际开发,作为Java工程师,都必须掌握NIO的原理和开发实战技能。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/927828.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-17
下一篇2022-05-17

发表评论

登录后才能评论

评论列表(0条)

    保存