Semaphone应用源码分析(二)

3.3.3 Semaphore公平实现

公平与非公平只是差了一个方法的实现tryAcquireShared实现
这个方法的实现中,如果是公平实现,需要先查看AQS中排队的情况

// 信号量公平实现
protected int tryAcquireShared(int acquires) {
// 死循环。
for (;;) {
// 公平实现在走下述逻辑前,先判断队列中排队的情况
// 如果没有排队的节点,直接不走if逻辑
// 如果有排队的节点,发现当前节点处在head.next位置,直接不走if逻辑if (hasQueuedPredecessors())
return -1;
// 下面这套逻辑和公平实现是一模一样的。
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}

3.3.4 Semaphore释放资源

因为信号量从头到尾都是共享锁的实现……
释放资源操作,不区分公平和非公平

// 信号量释放资源的方法入口
public void release() {
sync.releaseShared(1);
}
// 释放资源不分公平和非公平,都走AQS的releaseShared
public final boolean releaseShared(int arg) {
// 优先查看tryReleaseShared,这个方法是信号量自行实现的。
if (tryReleaseShared(arg)) {// 只要释放资源成功,执行doReleaseShared,唤醒AQS中排队的线程,去竞争Semaphore的资源
doReleaseShared();
return true;
}
return false;
}
// 信号量实现的释放资源方法
protected final boolean tryReleaseShared(int releases) {
// 死循环
for (;;) {
// 拿到当前的state
int current = getState();
// 将state + 归还的资源个数,新的state要被设置为next
int next = current + releases;
// 如果归还后的资源个数,小于之前的资源数。
// 避免出现归还资源后,导致next为负数,需要做健壮性判断
if (next < current)
throw new Error("Maximum permit count exceeded");
// CAS操作,保证原子性,只会有一个线程成功的就之前的state修改为next
if (compareAndSetState(current, next))
return true;
}
}

3.4 AQS中PROPAGATE节点

为了更好的了解PROPAGATE节点状态的意义,优先从JDK1.5去分析一下释放资源以及排队后获取资源的后置操作

3.4.1 掌握JDK1.5-Semaphore执行流程图

首先查看4个线程获取信号量资源的情况
[image]
往下查看释放资源的过程会触发什么问题
首先t1释放资源,做了进一步处理
[image]
当线程3获取锁资源后,线程2再次释放资源,因为执行点问题,导致线程4无法被唤醒

3.4.2 分析JDK1.8的变化

====================================JDK1.5实现============================================.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;}
return false;
}
private void setHeadAndPropagate(Node node, int propagate) {
setHead(node);
if (propagate > 0 && node.waitStatus != 0) {
Node s = node.next;
if (s == null || s.isShared())
unparkSuccessor(node);
}
}
====================================JDK1.8实现============================================.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
// 拿到head节点
Node h = head;
// 判断AQS中有排队的Node节点if (h != null && h != tail) {
// 拿到head节点的状态
int ws = h.waitStatus;
// 状态为-1
if (ws == Node.SIGNAL) {
// 将head节点的状态从-1,改为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 唤醒后继节点
unparkSuccessor(h);
}
// 发现head状态为0,将head状态从0改为-3,目的是为了往后面传播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 没有并发的时候。head节点没变化,正常完成释放排队的线程
if (h == head)
break;
}
}
private void setHeadAndPropagate(Node node, int propagate) {
// 拿到head
Node h = head;
// 将线程3的Node设置为新的head
setHead(node);// 如果propagate 大于0,代表还有剩余资源,直接唤醒后续节点,如果不满足,也需要继续往后判断看下是否需要传播
// h == null:看成健壮性判断即可
// 之前的head节点状态为负数,说明并发情况下,可能还有资源,需要继续向后唤醒Node
// 如果当前新head节点的状态为负数,继续释放后续节点
if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {
// 唤醒当前节点的后继节点
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}

4 Semaphore在Java并发编程中的使用和比较技术

Semaphore vs. Lock:

Semaphore和Lock(如ReentrantLock)都可以用于线程之间的互斥访问控制。它们的区别在于:

  • Semaphore允许多个线程同时访问资源,而Lock一次只允许一个线程访问资源。

  • Semaphore是基于计数的机制,可以控制同时访问的线程数量,而Lock只是简单的互斥锁。 根据具体场景,选择Semaphore还是Lock取决于对资源的访问控制需求。

Semaphore vs. Condition:

Semaphore和Condition都可以用于线程之间的同步和通信,但在不同的场景下有不同的用途:

  • Semaphore主要用于控制对资源的访问,限制并发线程的数量。

  • Condition主要用于线程之间的协调,可以通过await()和signal()等方法实现线程的等待和唤醒。

  1. Semaphore的适用场景: Semaphore在以下场景中特别有用:

  • 控制对有限资源的并发访问,如数据库连接池、线程池等。

  • 限制同时执行某个操作的线程数量,如限流和限制并发请求等。

  • 在生产者-消费者模式中平衡生产者和消费者之间的速度差异。

总结:

Semaphore是Java并发编程中一个强大的工具,用于控制对共享资源的访问和实现线程之间的同步。与其他并发控制机制相比,Semaphore具有灵活性和可扩展性。选择Semaphore还是其他机制取决于具体的需求和场景。在使用Semaphore时,需要注意正确地获取和释放许可,以避免死锁和资源争用等问题。

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

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

相关文章

paddle 55 使用Paddle Inference部署嵌入nms的PPYoloe模型(端到端fps达到52.63)

Paddle Inference 是飞桨的原生推理库,提供服务器端的高性能推理能力。由于 Paddle Inference 能力直接基于飞桨的训练算子,因此它支持飞桨训练出的所有模型的推理。paddle平台训练出的模型转换为静态图时可以选用Paddle Inference的框架进行推理,博主以前都是将静态图转换为…

科研学习|论文解读——面向电商内容安全风险管控的协同过滤推荐算法研究

【论文完整内容详见知网链接】&#xff1a; 面向电商内容安全风险管控的协同过滤推荐算法研究 - 中国知网 (cnki.net) 面向电商内容安全风险管控的协同过滤推荐算法研究* 摘 要&#xff1a;[目的/意义]随着电商平台商家入驻要求降低以及商品上线审核流程简化&#xff0c;内容安…

Centos安装vsftpd:centos配置vsftpd,ftp报200和227错误

一、centos下载安装vsftpd&#xff08;root权限&#xff09; 1、下载安装 yum -y install vsftpd 2、vsftpd的配置文件 /etc/vsftpd.conf 3、备份原来的配置文件 sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.backup 4、修改配置文件如下&#xff1a;vi /etc/vsftpd.conf …

go从0到1项目实战体系十三:全局/局部变量

1. 全局/局部变量: ①. 全局变量:a. C和Go语言中,定义在函数外面的就是全局变量.②. 局部变量:a. C和Go语言中,写在{}中、函数中、函数的形参,就是局部变量.b. 只能在{}里面有效.2. 作用域: ①. 全局变量:a. C语言中,全局变量的作用域是从定义的那一行开始,直到文件末尾为止.…

体验一下 CodeGPT 插件

体验一下 CodeGPT 插件 0. 背景1. CodeGPT 插件安装2. CodeGPT 插件基本配置3. (可选)CodeGPT 插件预制提示词原始配置(英文)4. CodeGPT 插件预制提示词配置(中文)5. 简单验证一下 0. 背景 看到B站Up主 “wwwzhouhui” 一个关于 CodeGPT 的视频&#xff0c;感觉挺有意思&#…

SpringMVC:整合 SSM 中篇

文章目录 SpringMVC - 04整合 SSM 中篇一、优化二、总结三、说明注意&#xff1a; SpringMVC - 04 整合 SSM 中篇 一、优化 在 spring-dao.xml 中配置 dao 接口扫描&#xff0c;可以动态地实现 dao 接口注入到 Spring 容器中。 优化前&#xff1a;手动创建 SqlSessionTempl…

HarmonyOS和OpenHarmony的区别

1.概要 众所周知&#xff0c;鸿蒙是华为开发的一款分布式操作系统。因为开发系统&#xff0c;最重要的是集思广益&#xff0c;大家共同维护。为了在IOS和Android之间生存&#xff0c;鸿蒙的茁壮成长一定是需要开源&#xff0c;各方助力才能实现。   在这种思想上&#xff0c;…

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…

登录shell与非登录shell、交互式与非交互式shell的知识点详细总结

一、登录shell与非登录shell 1.登录shell定义&#xff1a;指的是当用户登录系统时所取的那个shell&#xff0c;登录shell属于交互式shell。 登陆shell通常指的是&#xff1a;用户通过输入用户名/密码&#xff08;或证书认证&#xff09;后启动的shell.例如&#xff1a; 当时…

【docker笔记】docker常用命令

1、帮助启动类命令 1.1 启动、重启、查询当前状态、停止 systemctl start docker systemctl stop docker systemctl restart docker systemctl status docker1.2 设置开机启动 systemctl enable docker1.3 查看docker概要信息 docker info1.4 查看docker帮助文档 docker -…

python的故事

当Python这门编程语言诞生于上世纪90年代末的时候&#xff0c;很少有人能够预见到它将成为如今广受欢迎的一门语言。然而&#xff0c;正是由于其简洁、易读、强大的特性&#xff0c;以及活跃的开发社区&#xff0c;Python逐渐崭露头角&#xff0c;取得了长足的发展。 故事的开…

spring-validation实现分组校验

文章目录 前言实际开发可能会使用到分组校验maven添加依赖简单使用高级应用分组自定义分组组合分组 源码地址 前言 JSR 303中提出了Bean Validation&#xff0c;表示JavaBean的校验&#xff0c;Hibernate Validation是其具体实现&#xff0c;并对其进行了一些扩展&#xff0c;…

GO语言基础笔记(一):基本语法与数据类型

基本语法 变量声明&#xff1a; 使用 var 关键字&#xff0c;如 var x int 声明一个整型变量 x。简短声明方式&#xff0c;如 y : 0 直接初始化 y。 函数定义&#xff1a; 使用 func 关键字定义函数&#xff0c;如 func add(a int, b int) int { return a b }。 控制结构&…

Arduino上U8g2库显示中文的经历

u8g2自带很多中文库&#xff1b;但是向u8g2_font_wqy12_t_chinese3 比较全的应该是u8g2_font_wqy12_t_gb2312 这个&#xff0c;只是我还没有调用成功 这个库&#xff0c;中文就显示不全&#xff1b;有些没有定义&#xff0c;如百家姓 #include <Arduino.h> #include <…

Java经典框架之Spring

Java经典框架之Spring Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Spring简介 2.…

HA启动Advanced SSH Web Terminal 提示附加组件似乎尚未准备就绪,它可能仍在启动。是否要再试一次?

环境&#xff1a; Home Assistant OS11.1 Advanced SSH & Web Terminal 17.0 问题描述&#xff1a; HA安装好SSH加载项&#xff0c;启动Advanced SSH & Web Terminal 提示附加组件似乎尚未准备就绪&#xff0c;它可能仍在启动。是否要再试一次&#xff1f; 解决方案…

设计模式之-策略模式,快速掌握策略模式,通俗易懂的讲解策略模式以及它的使用场景

系列文章目录 设计模式之-6大设计原则简单易懂的理解以及它们的适用场景和代码示列 设计模式之-单列设计模式&#xff0c;5种单例设计模式使用场景以及它们的优缺点 设计模式之-3种常见的工厂模式简单工厂模式、工厂方法模式和抽象工厂模式&#xff0c;每一种模式的概念、使用…

.NET中的Swagger使用

目录 前言 一、Swagger是什么&#xff1f; 二、如何Swagger文档说明的信息 1.在AddSwaggerGen方法中写入文档信息 2.运行效果 二、文档UI界面标题、路由设置 1.在中间件UseSwaggerUI方法中配置 三、文档UI界面添加接口注释 1.在 .csproj中配置 2.在AddSwaggerGen方法中配置Incl…

Apache Spark简介与历史发展

在当今信息爆炸的时代&#xff0c;大数据处理已成为了现实。企业和组织需要处理海量数据来获得有用的信息和见解。Apache Spark作为一个开源的大数据处理框架&#xff0c;已经在大数据领域占据了重要地位。 Apache Spark简介 Apache Spark是一个用于大规模数据处理的快速、通…

【zookeeper特点】

文章目录 1. Zookeeper介绍2、ZooKeeper数据结构 1. Zookeeper介绍 ZooKeeper 是一个开源的分布式协调框架&#xff0c;是Apache Hadoop 的一个子项目&#xff0c;主要用来解决分 布式集群中应用系统的一致性问题。Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性 …