NIO之Selector

通俗编程——白话NIO之Selector Selector简介选择器提供选择执行已经就绪的任务的能力.从底层来看,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力。Selector 允许单线程处理多个Channel。 阅读详情

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

Selector的用途

  • 使用单个线程来处理多个通道。只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。因此,使用的线程越少越好。
    一个选择器来处理三个通道

Selector的创建

通过Selector.open()方法创建一个Selector,如:

Selector selector = Selector.open();

打开服务器套接字通道

ServerSocketChannel serverSocket = ServerSocketChannel.open();  
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 8099);  
serverSocket.bind(hostAddress);

向Selector注册通道

为了将Channel和Selector配合使用,需要将Channel注册到Selector上。通过selectableChannel.register()方法来实现。

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
  • 和Selector一起使用时,channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。
  • 注意register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:
  • 不同的Channel对象可以注册的事件是不一样的,可以通过public final int validOps()方法查看。
通道触发了一个事件意思是该事件已就绪。如下可以使用SelectionKey的四个常量来表示。
Connect:某个channel成功连接到另一个服务器称为“连接就绪”。可以用来表示 SelectionKey.OP_CONNECTS。
Accept:一个server socket channel准备好接收新进入的连接称为“接收就绪”。 SelectionKey_ACCEPT。
Read:一个有数据可读的通道可以说是“读就绪”。 SelectionKey.OP_READS。
Write:等待写数据的通道可以说是“写就绪”。 SelectionKey.OP_WRITE。
  • 如果你不止对一种事件感兴趣,可以使用位或操作符将SelectionKey常量连接 起来。如
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

SelectionKey

表示一个特定的通道和特定的选择器对象直接的注册关系。当向Selector中注册通道时,register()方法会返回一个SelectionKey对象。这个对象包含了一个你感兴趣的属性。

  • interest集合
  • ready集合
  • Channel
  • Selector
  • 附加的对象(可选)

interest集合

interest集合是你选择的感兴趣的事件集合。可以通过SelectionKey来读写interest集合,如下:

int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept  = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;

可以看到,用“位与”操作interest 集合和给定的SelectionKey常量,可以确定某个确定的事件是否在interest 集合中。

ready集合

ready 集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set。可以这样访问ready集合:

int readySet = selectionKey.readyOps();

可以用像检测interest集合那样的方法,来检测channel中什么事件或操作已经就绪。但是,也可以使用以下四个方法,它们都会返回一个布尔类型:

  • selectionKey.isAcceptable();
  • selectionKey.isConnectable();
  • selectionKey.isReadable();
  • selectionKey.isWritable();

Channel + Selector

从SelectorKey访问Channel和Selector很简单,如下

Channel channel = selectorKey.channel();
Selector selector = selectorKey.selector();

附加的对象

可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道。例如,可以附加与通道一起使用的Buffer,或是包含聚集数据的某个对象。使用方法如下:

selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();

还可以在用register()方法向Selector注册Channel的时候附加对象。如:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

通过Selector选择通道

一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道。换句话说,如果你对“读就绪”的通道感兴趣,select()方法会返回读事件已经就绪的那些通道。
下面是select()方法:

  • int select():阻塞到至少有一个通道在你注册的事件上就绪了。
  • int select(long timeout):和select()一样,但是只会阻塞timeout毫秒。
  • int selectNow():不会阻塞,不管什么通道就绪都立刻返回,如果没有通道就绪则返回0。
  • select()方法返回的int值表示有多少个通道已经就绪了。也就是从上次调用select()方法后有多少通道变成就绪状态。

selectedKeys()

一旦调用了select()方法,并且返回值表明有一个或更多的通道就绪了。就可以调用selector的selectedKeys()方法,访问这些已经就绪的通道了。

Set selectedKeys = selector.selectedKeys();

当像Selector注册Channel时,Channel.register()方法会返回一个SelectionKey 对象。这个对象代表了注册到该Selector的通道。可以通过SelectionKey的selectedKeySet()方法访问这些对象。

可以遍历这个已选择的键集合来访问就绪的通道,如下

Set selectedKeys = selectors.selectedKeys;
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
}
  • 这个循环遍历已选择键集中的每个键,并检测各个键对应的通道的就绪事件。
  • 注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道是手动移除。下次通道变成就绪是,Selector会再次将其放入选择集中。
  • SelectionKey.channel()方法返回的通道需要转型为我们直接需要的类型,如ServerSocketChannel或SocketChannel。

wakeup()

唤醒因为调用selector()方法阻塞的线程。如果当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立刻醒来。

close()

用来Selector调用close()方法会将其关闭,且是注册到该Selector的所有SelectionKey实例失效。通道本身不会关闭。

实例

package com.bzb.java8.main;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author bzb
 * @Description:
 * @date 2018/9/21 16:07
 */
public class ServerSelector {

    public static final int PORT = 9797;
    private static ByteBuffer buffer = ByteBuffer.allocate(1024);

    public static void main(String[] args) throws IOException {

        // 获取一个ServerSocket通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false); // 设置该通道为非阻塞的
        serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); // 将该通道对应的socket绑定到9797端口

        // 将serversocketchannel注册到Selector中
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册通道和感兴趣的accept事件

        System.out.println("register channel, channel number is:" + selector.keys().size());


        while (true) {
            // 阻塞直到通道有事件就绪
            int i = selector.select();
            if (i == 0) { // 无通道就绪,直接返回
                continue;
            }

            // 获取事件准备就绪的通道
            Set<SelectionKey> selectedKeys = selector.selectedKeys();

            // 轮询SelectedKeys处理就绪事件
            Iterator<SelectionKey> iterator = selectedKeys.iterator();

            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();

                if (key.isAcceptable()) {  // 如果满足Acceptable条件,则必定是一个ServerSocketChannel
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();

                    // 获取一个连接好的SocketChannel,并将它注册到Selector上,兴趣为READ
                    SocketChannel socketChannel = ssc.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("register channel, channel number is:" + selector.keys().size());
                } else if (key.isReadable()) { // 读取客户端传过来的数据并显示
                    SocketChannel socketChannel = (SocketChannel) key.channel();

                    buffer.clear();
                    while (socketChannel.read(buffer) > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        System.out.println("from client:" + new String(bytes));
                    }
                }

                // 清除处理完的key
                iterator.remove();
            }
        }

    }
}

package com.bzb.java8.main;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author bzb
 * @Description:
 * @date 2018/9/21 16:37
 */
public class ClientSelector {
    public static void main(String[] args) throws IOException, InterruptedException {
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress(ServerSelector.PORT));
        while (!channel.finishConnect()) {
            TimeUnit.MILLISECONDS.sleep(100);
        }
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        for (int i = 0; i < 5; i++) {
            TimeUnit.MILLISECONDS.sleep(100 * new Random().nextInt(10));
            String str = "Message from client, number:" + i;
            buffer.put(str.getBytes());
            buffer.flip();
            while (buffer.hasRemaining()) {
                channel.write(buffer);
            }
            buffer.clear();
        }
        channel.close();
    }
}

参考:并发编程网

手把手教你用SIMCOM A7670C_FASL模组实现双路MQTT连接阿里云(附完整源码) 本文详细介绍了如何使用SIMCOM A7670C_FASL 4G模组实现双路MQTT连接阿里云,包括硬件配置、开发环境搭建、双路MQTT架构设计及实现细节。通过完整的源码示例和实战指南,帮助开发者快速掌握物联网设备开发中的多路MQTT连接技术,提升工业级应用的开发效率。 阅读详情

相关推荐

重装系统后,恢复Oracle的.ORA文件

前几天,系统莫名奇妙产生很多宏病毒,进而感染整个C盘,不得已,格式化后重装了系统。 重装系统之后,突然想到D盘中的oracle中很多数据没导出来备份,安装文件下有很多有用的.ORA文件,抱着死马当活马医的态度,居然成功了。 1、将原来D盘(D:\oracle\product\10.2.0\oradata\orcl)下的数据文件备份下其他盘,将D盘的oracle目录名改成oracle1。安装数据

zhanggnol的blog 5551

理解NIO中的Selector

Selector是选择器是NIO技术中的核心组件,可以将通道注册进选择器中,其主要作用就是使用一个线程来对多个通道中的已就绪通道进行选择,然后就可以对选择的通道进行数据处理,属于一对多的关系。这种机制在NIO技术中心称为“IO多路复用”。其优势是可以节省CPU资源 说的简单点就是一个线程通过选择器可以连接多个通道,从而完成高效的I/O 在Selector中有三个核心类: Selector:主...

Yan1less的小窝 1061

Windows下RISC-V开发环境搭建:xpack-riscv-none-elf-gcc安装配置全攻略

本文详细介绍了在Windows平台下搭建RISC-V开发环境的完整流程,重点讲解xpack-riscv-none-elf-gcc工具链的安装配置方法。从环境变量设置到VSCode集成开发环境搭建,涵盖汇编程序和C程序开发实践,为开发者提供一站式解决方案,助力快速掌握RISC-V架构开发技能。

weixin_26765287的博客 437

NIO(三) Selector使用(NIO综合)

比如在第一次的selector对某个Channel的read和write操作感兴趣,在第一次执行select()时,该通道的read操作就绪,此时该通道对应的键中的ready集合存有read元素,在第二次执行select()时,该通道的write操作也就绪了,此时该通道对应的ready集合中将同时有read和write元素。1,检查已取消键集合(执行过selectionKey.cancel会添加进来),如果该集合不为空,则清空该集合里的键,同时该集合中每个键也将从已注册键集合和已选择键集合中移除。

jiezheee的专栏 1475

ios快捷指令进行钉钉打卡

首先 1、打开快捷指令 2、自动化 3、创建个人自动化-特定时间 4、添加操作-搜索“随机数” 5、最小值和最大值填充 6、添加操作-搜索“等待” 7、长按“1秒钟”,选择魔法变量,选择上一步的随机数 6、添加操作-搜索“钉钉” 7、选择钉钉-随便一个聊天窗口。 8、添加操作-搜索“等待”,这次将等待时间设为10秒 9、添加操作 “搜索天气” ,选择显示天气。 注意点: 1、此操作依赖于钉钉极速打卡,只是帮忙唤起钉钉。 2、保存自动化时有个运行前询问,务必关掉。 3、手机不能设

blaze_jack 5万+

《Cesium 基础知识点》- 申请 Cesium Ion 密钥

一、访问 https://cesium.com/ion/signin/tokens 二、登录/注册 三、申请 四、使用 // 密钥 let defaultAccessToken = 'your token string'; // 设置 Cesium.Ion.defaultAccessToken = defaultAccessToken; // 初始化 let viewer = new Cesium.Viewer('cesiumContainer', {}); ...

前端技术 2570

NIO三个核心组件 ---- selector(选择器,也叫多路复用选择器)

selector的作用: 1.Java NIO 用非阻塞的IO方式,可以用一个线程,处理多个客户端连接,就会用到selector 2.Selector能够检测多个注册的通道上是否有事件发生,如果有时间发生,便获取时间然后针对每个时间进行对应的处理,这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求 3.只有在连接真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每...

jym的博客 9860

基于YOLOv13+CBAM注意力机制

本教程详细介绍了CBAM注意力机制的原理及其在YOLOv13中的集成方法。CBAM通过通道注意力和空间注意力两个模块,动态调整特征权重以增强模型性能。通道模块利用池化操作和共享MLP生成通道权重,空间模块则通过池化和卷积生成空间权重。教程提供了在YOLOv13中实现CBAM的具体步骤,包括模块代码定义、模型配置修改等关键环节,帮助开发者在目标检测任务中提升模型表现。

FJN110的博客 305

NIO 三大组件之Selector

一、Selector 简介 Selector 一般称其为“选择器”,在Java NIO中起着多路复用的作用。它可用于检查一个或多个Channel(通道)的状态,是否处于可读、可写。如此便可实现单个线程管理多个channels,也就可以管理多个网络链接。因此使用Selector 的好处便是使用更少的线程来就可以来处理多个通道了,相比传统I/O,这里也就避免了线程上下文切花的开销了。这里看下Selector类的代码: public abstract class Selector implements Cl

寒山道杳 1355

【路径规划】Theta_star算法机器人栅格地图最短路径规划【含Matlab源码 2618期】

Theta_star算法机器人栅格地图最短路径规划 完整的代码,方可运行;可提供运行操作视频!适合小白!

订阅付费专栏Matlab(奶茶价版),可赠送奶茶价版付费专栏指定代码1份; 363

【沃顿商学院学习笔记】商业基础——Financing:05 名义利率和实际利率 APR& EAR

商业基础——名义利率和实际利率 本章主要是从名义利率和实际利率的相关概念和计算方法进行学习。 名义利率和实际利率 APR&amp;amp; EAR 根据下图可以直到: APR,QUOTED RATE在精算中又叫名义利率,比如一个房产贷款quoted rate or APR是12%,且按月复利,那么说明它的月实际利率是12%/12=1% EAR是effecive annual rate,在精算中叫实际利率,...

ECNU乐扣老师——区块链、可计算情感 1万+

NIO之Selector的使用

Selector的使用

z318913的博客 1098

Linux ARM平台开发系列讲解(调试篇) 1.6.4 NVIDIA AGX Xavier以太网MAC TO MAC模式

内核发展了那么多年,早已经将这种场景集成到了内核里,具体添加方式如下,本次针对。具体详细描述,可参考本专栏的网络篇和交换机篇,这里就简单的提一句,想要使用。速率和双工状态固定死,使其和交换机的模式一模一样,双方就可以建立通信了。2. MAC TO MAC原理。3. 开启无PHY模式进入内核。模式,本章节记录调试过程。模式,简单来说就是字面意思,以太网部分需要与交换机。,其他芯片也是一样的。修改设备树文件,将与。

DSMGUOGUO的博客 1530

Java NIO Selector选择器

Selector是Java NIO中的一个关键组件,用于单线程管理多个网络通道(Channel),检查它们是否处于可读、可写等状态。通过使用Selector,可以减少线程数量,降低系统资源消耗,因为线程切换在操作系统中是开销较大的操作。Selector通过Selector.open()创建,并通过register()方法将非阻塞的Channel注册到Selector上,监听连接、接收、读、写等事件。Selector的select()方法用于检测就绪的Channel,返回就绪的Channel数量,并通过sel

Java的专栏 1054

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

NIO之旅Selector

之前的文章介绍了Buffer和Channel,今天这篇文章简单聊一下Selector。 Selector是NIO中最为重要的组件之一,我们常常说的多路复用器就是指的Selector组件。Selector组件用于轮询一个或多个NIO Channel的状态是否处于可读、可写。通过轮询的机制就可以管理多个Channel,也就是说可以管理多个网络连接。 轮询机制 首先,需要将Channel注册到Selector上,这样Selector才知道需要管理哪些Channel 接着Selector会不断轮询其上注册的Ch

程铭程铭你快成名的博客 7706

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Java NIO Selector

为什么使用Selector 使用一个线程来处理多个Channels的优势在于你只需要使用少量的线程就能处理大量的Channels。实际上,你可以只用一个线程来处理所有的Channels。线程切换对于操作系统来说代价昂贵,而且每个线程也会占用操作系统的一些资源(比如内存),因此你使用的线程数量越少越好。 但请记住,现代操作系统和CPU在多任务处理方面已经变的越来越强,所以随着时间的推移,在多线程

晴空的专栏 5062

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Java NIO编程实例之三Selector

Java NIO主要包含三个概念,即缓冲区(Buffer)、通道(Channel)和选择器(Selector)。前面的文章已经介绍了缓冲区和通道,本文则讲述最复杂的选择器Selector。 本文是本系列的第三篇文章,关于缓冲区Buffer可以看第一篇: https://zhuanlan.zhihu.com/p/25701512 关于通道Channel可以看第二篇: https://zhuan

LogicTeamLeader的专栏 2594

深入浅出NIO之Selector实现原理

前言 Java NIO 由以下几个核心部分组成: 1、Buffer 2、Channel 3、Selector Buffer和Channel在深入浅出NIO之Channel、Buffer一文中已经介绍过,本文主要讲解NIO的Selector实现原理。 之前进行socket编程时,accept方法会一直阻塞,直到有客户端请求的到来,并返回socket进行相应的处理。整个过程是流水线的,

Java烂猪皮 5194

NIO - selector简单介绍

selector作为NIO当中三大组件之一,是处理NIO非阻塞模式下的核心组件,它允许一个单个线程管理多个通道。

2303_79546217的博客 1290

NIO之Selector详解

转载自:https://blog.youkuaiyun.com/yunzhaji3762/article/details/79154525 Java NIO 由以下几个核心部分组成: 1、Buffer 2、Channel 3、Selector Buffer和Channel在深入浅出NIO之Channel、Buffer一文中已经介绍过,本文主要讲解NIO的Selector实现原理。 之前进行socket编程时...

weixin_41588489的博客 357

【Java NIO】— 详解 Selector

在前一篇文章中码哥分析了 ServerSocketChannel 两种模式的缺点阻塞模式:所有阻塞方法都会引起线程的暂停,根本无法应用到业务中来非阻塞模式:CPU 一直在空转,浪费资源所以,如果是我们服务端单独使用 ServerSocketChannel 确实是很麻烦,典型的吃力不讨好。故而我们希望有一个组件能够统一管理我们的 Channel,这个组件就是选择器 Selector。

smart_an的专栏 1194

四、Java NIO Selector

Java NIO Selector

梦想成真 1893

Java NIO 之selector

1. 什么是selector 2. 怎么用selector 3.  为什么selector

Just Do It! 496

介绍 Java NIO Selector

介绍 Java NIO Selector 文本我们探讨java NIO引入Selector组件。 selector提供了监控多个NIO channel机制,并识别何时有一个或多个channel可用来传输数据。通过这种方式,单个线程可以用于管理多个channel,从而管理多个网络连接。 为什么使用Selector? 使用selector,让一个线程可以管理多个channel,而不是多个线程。多个线程...

neweastsun的专栏 567

Java NIO类库Selector机制解析(下)

 Java NIO类库Selector机制解析(下) 赵锟   陈皓http://blog.youkuaiyun.com/haoel 点此查看本文上篇五、  迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel (文件描述符) ,

陈皓专栏 【空谷幽兰,心如皓月】 4万+
上一篇: NIO之通道之间的数据传输
下一篇: JavaNIO之Pipe
菜鸟=大神
博客等级 码龄11年 5粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值