栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Java NIO之Selector多路复用器

Java 更新时间:发布时间: 百科书网 趣学号

文章目录
  • 回顾一下
  • 一、Selector 是什么?
  • 二、代码示例
    • 1.打开一个ServerSocketChannel
    • 2.选择select
    • 3.处理事件集合
    • 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事件,表示服务器监听到了客户连接,那么服务器可以接收这个连接了,还有一些其他事件:

  1. 读就绪: SelectionKey.OP_READ
  2. 写就绪: SelectionKey.OP_WRITE
  3. 连接就绪: 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 selectionKeys = selector.selectedKeys();
            Iterator 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的原理和开发实战技能。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/889273.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号