Java Nio中Selector是什么?Selector怎么使用呢?

以前大家都是用阻塞式IO来对网络IO进行数据请求,对于不同的IO都要分配一个线程来处理,如果没有数据就会进行等待,从而造成了阻塞,这种方式极大地浪费了资源(如图1)。于是,有人就提出了一个想法,使用一个线程去监控多个IO请求,如果哪一个IO数据准备完毕后就通知相应的线程来处理,这就是selector模型(如图2)。而Java中的selector就是对selector模型的一种实现,用于询问选择已就绪的IO处理任务。

image

Selector的几个核心的概念

  1. Channel(通道):用于进行网络传输的通道,网络传输的数据都放在通道中,可以进行写入,也可以进行读取。Channel主要有两种:ServerSocketChannel和SocketChannel,其中ServerSocketChannel是用于服务端开发的,而SocketChannel是用于客户端开发的。
  2. Selector(选择器):用于进行监控多个通道数据状态。
  3. SelectableChannel(可选择通道):可以被选择器选择的通道,继承了抽象类SelectableChannel的Channel,而FileChannel没有继承此类,所以不可以被选择器选择。
  4. SelectionKey(选择键):用于表示通道可以被选择的某种就绪事件状态。选择键的事件主要有以下几种:
    OP_READ:可读事件。 OP_WRITE:可写事件。 OP_CONNECT:客户端连接服务端的事件,一般为创建SocketChannel客户端channel。 OP_ACCEPT:服务端接收客户端连接的事件,一般为创建ServerSocketChannel服务端channel。

Selector的使用

1.创建选择器

Selector selector = Selector.open();

2.获取通道

ServerSocketChannel channel = ServerSocketChannel.open(); // 创建通道
channel.bind(new InetSocketAddress(8080));  // 绑定端口
channel.configureBlocking(false); // 设置为非阻塞,注册到selector上的通道一定设置为非阻塞,否则会报IllegalBlockingModeException错误

3.将通道注册到选择器上

channel.register(selector, SelectionKey.OP_ACCEPT); // 将通道注册到选择器上,监听可接收事件,对于监听多个事件可以用“按位或”来操作

4.轮询已就绪的事件,并对不同的事件进行处理

while (true) {
    int count = selector.select();  // 获取已就绪事件的数量
    if (count == 0) {
        continue;
    }
    Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 获取已就绪键集
    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();           
        if(key.isAcceptable()) {
            // 接收处理
        } else if (key.isConnectable()) {
            // 连接处理
        } else if (key.isReadable()) {
            // 读取处理
        } else if (key.isWritable()) {
            // 写入处理
        }
        key.remove(); // 移除键,防止下次重复处理
    }
}             
// 注意:key.isWritable只要建立连接,就会触发,所以在设置可写入事件时,在写入之后要改回可监听事件,否则就会死循环

其他关于Selector的知识点

Selector内部总共维护了三组键集:

keys:当前Channel注册在Selector上面的所有的key,可以调用keys()获取。

selectedKeys:当前Channel所有已就绪的事件,可以调用selectedKeys()获取。

cancelledKeys:当前Channel所有已取消的事件,主动调用cancel()方法的事件会放在该集合。

其他一些常用的方法:

  • Selector#isOpen():判断selector是否是open状态,如果调用了close()方法则会返回false
  • SelectionKey#isValid():判断选择键是否有效。
  • Selector#selectNow():获取是否有就绪的事件,该方法立即返回结果,不会阻塞。
  • Selector#select(long timeout):在超时时间内,有就绪事件时才会返回,其次超过时间也会返回。
  • Selector#select():阻塞直到有事件就绪时才会返回
  • Selector#wakeup():调用该方法会时,阻塞在select()处的线程会立即返回。即使当前不存在线程阻塞在select()处,那么下一个select()方法也会立即返回。
  • Selector#close():用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。

一个Selector的简单示例

/**
    仅服务端,客户端可以使用telnet命令
**/
@Test
public void testSelector() throws IOException, InterruptedException {
    ServerSocketChannel channel = ServerSocketChannel.open(); // 创建通道
    channel.bind(new InetSocketAddress(8080)); // 绑定端口号
    channel.configureBlocking(false);   // 设置为非阻塞
    Selector selector = Selector.open(); // 创建选择器
    channel.register(selector, SelectionKey.OP_ACCEPT); // 注册到选择器上

    Thread thread = new Thread(() -> {
        try {
            while (true) {
                int count = selector.select(); // 获取已就绪事件数量
                if (count == 0) {
                    continue;
                }
                Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 获取已就绪键集
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (!key.isValid()) {  // 判断选择键是否有效
                        continue;
                    } else if (key.isAcceptable()) {  // 处理接收事件
                        SocketChannel socket = channel.accept();
                        socket.configureBlocking(false);
                        socket.register(selector, SelectionKey.OP_READ);
                        System.out.println("已注册" + socket);
                    } else if (key.isReadable()) {  // 处理读取事件 
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);  // 声明buffer
                        socketChannel.read(byteBuffer);  // 将通道中的数据读取到buffer
                        byteBuffer.flip();  
                        byte[] bytes = new byte[byteBuffer.remaining()];
                        byteBuffer.get(bytes);
                        String str = new String(bytes);
                        str = "\r\nreceive: " + str;
                        System.out.println(str);
                        socketChannel.write(ByteBuffer.wrap(str.getBytes())); // 将数据写回通道中
                    }
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
    thread.start();
    thread.join();
}

专栏部分持续更新中,请收藏或关注我哦!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容