Spring架构篇--2.5.4 远程通信基础--Select 源码篇--selector.close()总结

news2024/11/26 20:31:19

前言:通过selector 的poll 来完成所有socket 事件的监听,当不需要selector时 通过selector.close() 完成通道的关闭和资源的释放;

1 selector.close()关闭:

AbstractSelector 类中close 方法:

public final void close() throws IOException {
    boolean open = selectorOpen.getAndSet(false);
    if (!open)
        return;
    implCloseSelector();
}

selectorOpen 是原子类型变量,并发时,只有一个线程能继续向下执行 implCloseSelector;
在看implCloseSelector:

public void implCloseSelector() throws IOException {
	 // 唤醒主线程
     this.wakeup();
     // 按照select() 加锁方向依次获取锁
     synchronized(this) {
         synchronized(this.publicKeys) {
             synchronized(this.publicSelectedKeys) {
                 this.implClose();
             }
         }

     }
 }

首先会调用 wakeup 唤醒选择器。这是有必要的,假如当前选择器正在做选择操作,如果不进行唤醒操作,由于 select 是阻塞的,这就可能会导致 close 被阻塞。回想一下 select 的加锁顺序,this->publicKeys->publicSelectedKeys,close 采用了通用的加锁顺序。因此调用 wakeup 唤醒选择器,当选择器 select 调用释放了相关的锁资源,close 才能顺序进行。

在看WindowsSelectorImpl 下的 this.implClose():

// private Object closeLock = new Object();
protected void implClose() throws IOException {
   synchronized(this.closeLock) {
   		// 获取closeLock 锁
        if (this.channelArray != null && this.pollWrapper != null) {
            synchronized(this.interruptLock) {
                this.interruptTriggered = true;
            }
			// 管道非空,关闭之前创建用于唤醒主线程的通道
            this.wakeupPipe.sink().close();
            this.wakeupPipe.source().close();
			// 
            for(int var2 = 1; var2 < this.totalChannels; ++var2) {
            	// 遍历 totalChannels,调用 deregister 从通道的键集合中注销相应的选择键
                if (var2 % 1024 != 0) {
                    this.deregister(this.channelArray[var2]);
                    SelectableChannel var3 = this.channelArray[var2].channel();
                    if (!var3.isOpen() && !var3.isRegistered()) {
                    // 若通道已经关闭并且没有注册到其他选择器上,调用 kill () 关闭通道
                        ((SelChImpl)var3).kill();
                    }
                }
            }
			// 释放pollWrapper 资源
            this.pollWrapper.free();
            // 并将 pollWrapper,selectedKeys ,channelArray 引用置为 null便于gc回收
            this.pollWrapper = null;
            this.selectedKeys = null;
            this.channelArray = null;
            // 释放辅助线程将所有辅助线程设为空闲
            Iterator var7 = this.threads.iterator();

            while(var7.hasNext()) {
                WindowsSelectorImpl.SelectThread var8 = (WindowsSelectorImpl.SelectThread)var7.next();
                var8.makeZombie();
            }
			//  startLock.startThreads () 唤醒所有辅助线程,退出SelectThread 中线程的 run 方法
            this.startLock.startThreads();
        }

    }
}
protected final void deregister(AbstractSelectionKey key) {
    ((AbstractSelectableChannel)key.channel()).removeKey(key);
}

close 负责关闭选择器,并且释放相应的资源。首先加 closeLock,如果 channelArray 和 pollWrapper 非空,首先关闭 wakeupPipe 的 sink 通道和 source 通道;接下来遍历 totalChannels,调用 deregister 从通道的键集合中注销相应的选择键,若通道已经关闭并且没有注册到其他选择器上,调用 kill () 关闭通道;然后释放 pollWrapper,并将 pollWrapper,selectedKeys ,channelArray 引用置为 null;最后遍历 threads 集合,将所有辅助线程设为空闲,并调用 startLock.startThreads () 唤醒所有辅助线程,退出 run 方法。

2 selector 回顾:

public static void main(String[] args) {
   // Channel / Buffer  / Selector
    try {
    	// window 环境下,通过Selector.open() 完成WindowsSelectorImpl 实例的构建
        selector = Selector.open();
        // ServerSocketChannel.open() 只是做了初始化所以 后面要自己在增加 socket 的创建
        // 及对端口的监听
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        // 连接事件注册到多路复用
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // select 最终通过poll() 方法向内核询问,是否有通道准备好,
            // 如果所有的通道都没有准备好,进行阻塞,并让出cpu 资源
            selector.select();
            // 程序执行到此说明有管道已经做好了准备,取出所有已经准备好的通道
            Set<SelectionKey> selectionKeySet = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeySet.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 根据通道的事件类型分别做业务处理
                if (selectionKey.isAcceptable()) {
                    // 有客户端连接事件
                    handleAcceptEvent(selectionKey,"selector");
                } else if (selectionKey.isReadable()) {
                    // 有客户端写事件--》服务端进行读取
                    handleReadEvent(selectionKey,"selector");
                } else if (selectionKey.isWritable()) {
                    System.out.println("\"server write\" = " + "server write");
//
                }
                // selectionKey.cancel();
                iterator.remove();
                // System.out.println("selectionKeySet.size() = " + selectionKeySet.size());
                Thread.sleep(3000);
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    // 最终关闭所有通道,释放资源
	selector.close();
}

3 总结:

在这里插入图片描述

  • 通过selector.open 建立管道并记录管道信息;
  • register将通信管道感兴趣的事件通过注册到selector ;
  • 在每次select()时 ,通过poll()函数向内核发起管道是否准备就绪事件,如果没有事件则阻塞select() 让出cpu资源,当有通道准备就绪,通过wakeup 唤醒所有线程 ,并将新的就绪事件添加到selectedKeys 中后返回;
  • 最终通过轮询所有SelectionKey 对已经就绪的管道进行处理;

参考:
1 Selector 源码深入分析之 Window 实现(下篇);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单说说OSI网络七层模型

如果你读过计算机专业&#xff0c;或者学习过网络通信&#xff0c;那你一定听说过 OSI 模型&#xff0c;它曾无数次让你头大。OSI 是 Open System Interconnection 的缩写&#xff0c;译为“开放式系统互联”。OSI 模型把网络通信的工作分为 7 层&#xff0c;从下到上分别是物理…

你知道这几种常见的JVM调优场景吗?

看此文前需已了解了运行时的数据区域和常用的垃圾回收算法&#xff0c;也了解了Hotspot支持的垃圾回收器。 一、cpu占用过高 cpu占用过高要分情况讨论&#xff0c;是不是业务上在搞活动&#xff0c;突然有大批的流量进来&#xff0c;而且活动结束后cpu占用率就下降了&#xf…

WebGL和OpenGL的区别及关系

什么是WebGLWebGL™是一个跨平台的&#xff0c;免版税的开放Web标准&#xff0c;用于基于OpenGL ES的低级3D图形API&#xff0c;通过HTML5 Canvas元素向ECMAScript公开。熟悉OpenGL ES 2.0的开发人员将使用GLSL将WebGL识别为基于Shader的API&#xff0c;其构造在语义上与底层Op…

FISCO BCOS(二十七)———java操作WeBase

一、搭建fiscobcos环境 1.1、安装jdk1.8 https://blog.csdn.net/weixin_46457946/article/details/1232435131.2、安装mysql https://blog.csdn.net/weixin_46457946/article/details/1232447361.3、安装python https://blog.csdn.net/weixin_46457946/article/details/123…

Lookup argument总览

1. 引言 详情参看Ingonyama团队Tomer 2023年论文《A Brief History of Lookup Arguments》。 Bootle等人2018年论文《Nearly linear-time zero-knowledge proofs for correct program execution》中首次提出了lookup协议&#xff0c;用于证明如下statement&#xff1a; 此处…

Python socket之TCP通信、下载文件

TCP简介TCP介绍TCP协议&#xff0c;传输控制协议&#xff08;英语&#xff1a;Transmission Control Protocol&#xff0c;缩写为 TCP&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由IETF的RFC 793定义。TCP通信需要经过创建连接、数据传送、…

手撕八大排序(上)

排序的概念及其引用&#xff1a; 排序的概念&#xff1a; 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有…

Linux上基于PID找到对应的进程名以及所在目录

Linux上基于PID找到对应的进程名以及所在目录前言找到进程的pid通过top命令查看通过 ps -ef |grep nignx进行查看通过端口号进行查看查看nginx进程目录前言 在一台新接触的服务器&#xff0c;却不熟悉搭建所在目录的时候&#xff0c;这时候就就可以通过ps查找进程&#xff0c;并…

巧用性格上的差异来组建团队

你好&#xff0c;我是得物 App 交易平台及中间件平台的 Team Leader Alan。 组建团队过程中&#xff0c;你有没有遇到过类似的场景&#xff1a;团队中某些人之间总是互相不对付、气场不合&#xff0c;不管是日常沟通中还是方案对齐&#xff0c;总是会出现面红耳赤的场面。 从…

Linux_线程概念

进程回顾 在学习线程之前&#xff0c;我们先回顾一下之前讲的进程概念 当我们创建一个进程&#xff0c;操作系统会将磁盘中的代码load到内存中&#xff0c;然后创建当前进程的task_struct&#xff08;后面可能会用”PCB“或者”进程控制块“代替&#xff09;&#xff0c;创建…

egg+vue实现登录功能【解决vue中登录的潜在问题】

前后端登录功能实现前言一、cookie和session二、代码呈现1.egg部门代码2.vue代码过程前言 记忆在时间面前总是不堪一击&#xff01; 本人的记录&#xff0c;下面内容仅供参考&#xff0c;如有什么什么&#xff0c;请自行解决。 一、cookie和session 不多赘述&#xff0c;详情…

大数据Hadoop教程-学习笔记03【Hadoop MapReduce与Hadoop YARN】

视频教程&#xff1a;哔哩哔哩网站&#xff1a;黑马大数据Hadoop入门视频教程教程资源: https://pan.baidu.com/s/1WYgyI3KgbzKzFD639lA-_g 提取码: 6666【P001-P017】大数据Hadoop教程-学习笔记01【大数据导论与Linux基础】【17p】【P018-P037】大数据Hadoop教程-学习笔记02【…

风险的定义以及CAPM 和 APT

文章目录风险定义&#xff1a;CAPMAPT&#xff08;Arbitrage Pricing Theory&#xff09;套利定价模型风险定义&#xff1a; 投资组合的收益率等于组合中各资产收益率的加权平均&#xff0c;但是投资组合的标准差并不等于组合中各资产标准差的加权平均&#xff0c;而是小于等于…

Git 详细教程

目录1.简介&#xff1a;2.安装Git3.Git 如何工作状态区域4.使用Git5.Git配置5.1 创建仓库 - repository5.2 配置5.2.1 --global5.2.2 检查配置6. 查看工作区的文件状态6.1什么是工作区6.2 如果显示乱码的解决方式7.在工作区添加单个文件8. 添加工作区文件到暂存区9. 创建版本10…

数据结构与算法(二)(Python版)

数据结构与算法&#xff08;一&#xff09;&#xff08;Python版&#xff09; 文章目录递归动规初识递归&#xff1a;数列求和递归三定律递归的应用&#xff1a;任意进制转换递归的应用&#xff1a;斐波那契数列递归调用的实现分治策略与递归优化问题和贪心策略找零兑换问题贪心…

RocketMQ-基本概念

主题&#xff08;Topic&#xff09; Apache RocketMQ 中消息传输和存储的顶层容器&#xff0c;用于标识同一类业务逻辑的消息。主题通过TopicName来做唯一标识和区分。 主题的作用主要如下&#xff1a; 定义数据的分类隔离&#xff1a; 在 Apache RocketMQ 的方案设计中&…

挚文集团短期内不适合投资,长期内看好

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 挚文集团&#xff08;MOMO&#xff09;在新闻稿中称自己是“中国在线社交和娱乐领域的领军企业”。 该公司旗下的陌陌是中国“陌生人社交网络”移动应用类别的领导者&#xff0c;并在2022年9月拥有超过1亿的月活跃用户。探…

Eotalk Vol.03:结合 API DaaS,让使用数据更方便

Eotalk 是由 Eolink CEO 刘昊臻发起的泛技术聊天活动&#xff0c;每期都会邀请一些技术圈内的大牛聊聊天&#xff0c;聊些关于技术、创业工作、投融资等热点话题。 Eotalk 的第 3 期&#xff0c;很高兴邀请到 Tapdata CEO TJ 唐建法&#xff0c;TJ 可以说是一位超级大咖&#x…

ESP32-C3 BLE5.0 扩展蓝牙名称长度的流程

蓝牙设备名称长度受限于蓝牙广播数据包的长度&#xff0c;如果广播数据包的长度不能包含完整的设备名称&#xff0c;则只显示短名称&#xff0c;其余不能容纳的部分将被截断。ESP32-C3 支持 BLE5.0&#xff0c;最大广播包长支持 1650 字节&#xff0c;可通过 esp_ble_gap_confi…

Windows下SecureCRT的下载、安装、使用、配置【Telnet/ssh/Serial】

目录 一、概述 二、SecureCRT的下载、安装 三、SecureCRT的使用  &#x1f449;3.1 使用SSH协议连接Linux开发主机  &#x1f449;3.2 使用Serial(串口)协议连接嵌入式开发板  &#x1f449;3.3 使用Telnet协议连接嵌入式开发板 四、SecureCRT配置会话选项  &#x1f3a8;4…