【同步工具类:Semaphore】

news2024/9/30 21:32:42

同步工具类:Semaphore

  • 介绍
  • 源码分析
    • 构造函数
    • acquire 获取信号量
    • release 释放信号量
  • 业务场景
    • 代码
    • 测试结果
  • 总结

介绍

官方说明:
Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。
通俗说明:
Semaphore 中文俗称信号量,主要用于控制流量,比如:数据库连接池给你分配10个链接,那么让你来一个连一个,连到10个还没有人释放,那你就等等。

源码分析

构造函数

permits 可以理解为许可证,默认情况下只需要传入permits即可。也就是一次运行放行几个线程。如果你需要使用Semaphore 共享锁中的公平锁,那么可以传入第二个构造函数fair= false/true.

public Semaphore(int permits) {
		//permits 为许可数量,默认构造非公平版本
        sync = new NonfairSync(permits);
    }

public Semaphore(int permits, boolean fair) {
		//permits 为许可数量,fair 来决定构造公平版本还是非公平版本
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

acquire 获取信号量

如下所示,其实获取信号量的这四个方法,主要就是,一次获取几个和是否响应中断的组合。
是否响应中断 可以理解为 其他线程调用interrupt方法时,该线程是否做出响应(一般是抛出异常)。
下面举例了 acquire()方法。调用了AQS的模板方法 acquireSharedInterruptibly();其中Semaphore 实现了公平锁和非公平锁的tryAcquireShared()方法。公平锁的是首先判断队列里面有没有排队的。如果有的话,就需要等待阻塞。非公平锁 是一上来就死循环 去拿资源。

方法描述
semaphore.acquire()一次获取一个信号量,响应中断
semaphore.acquire(2)一次获取n个信号量,响应中断(一次占2个坑)
semaphore.acquireUninterruptibly()一次获取一个信号量,不响应中断
semaphore.acquireUninterruptibly(2)一次获取n个信号量,不响应中断
 public void acquire() throws InterruptedException {
         //调用AQS 的模板方法
        sync.acquireSharedInterruptibly(1);
    }
 public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
            //此处响应中断,抛出InterruptedException()
        if (Thread.interrupted())
            throw new InterruptedException();
            //Semaphore 的公平锁和非公平锁都实现了tryAcquireShared方法
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

公平锁:

     protected int tryAcquireShared(int acquires) {
            for (;;) {
                //调用AQS的 hasQueuedPredecessors方法,查看是否
                //有其他线程排队,有的话就返回-1,不获取资源,直接进入阻塞
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                //获取的资源需要小于剩余的资源
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

非公平锁:

protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                //判断资源是否够用
                int remaining = available - acquires;
                //够用的话直接 cas 修改返回
                //不够用的话也是cas 修改
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

release 释放信号量

有获取就得有释放,获取了几个信号量就要释放几个信号量

方法描述
semaphore.release()一次释放一个信号量
semaphore.release(2)一次获取n个信号量
public void release() {
		//调用AQS的模板方法 releaseShared()
        sync.releaseShared(1);
    }
  public final boolean releaseShared(int arg) {
  		//Semaphore Sync 实现了tryAcquireShared方法
  		//公平锁和非公平锁都是一样的逻辑
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                //对参数进行校验,首先不能是负数
                //其次不能两数相加超过了Integer.MaxValue 从而溢出了
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                    //在循环里面将资源释放回去即可
                if (compareAndSetState(current, next))
                    return true;
            }
        }

业务场景

此处模拟8个人同时去上厕所,但是测试只有两个。所以需要排队同步,此时可以利用Semaphore。

代码

/**
 * @author :echo_黄诗
 * @description:Semaphore 的业务模拟
 * @date :2023/3/1 17:11
 */
public class Demo {
    public static void main(String[] args) {
        //定义两个资源(此时是厕所)
        Semaphore semaphore=new Semaphore(2);
        //定义核心线程池个数为8
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(8,20,20,
              TimeUnit.SECONDS,new ArrayBlockingQueue(15));
        //定义8个人来上厕所
      for (int i=0;i<8;i++){
          threadPoolExecutor.execute(()->{
              try {
                  semaphore.acquire();
                  System.out.println(new SimpleDateFormat("YYYY-MM-DD HH:mm:ss").format(new Date()));
                   //模拟上厕所耗时
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }finally {
                  //上完厕所后,需要离开(释放资源)
                  semaphore.release();
              }
              System.out.println(Thread.currentThread().getName()+": 来上厕所了,");
          });
      }
    }
}

测试结果

在这里插入图片描述

总结

一:Semaphore 类的核心是Sync,它继承了AQS类,并重写了几个关键方法来实现自己的特殊功能。
二:Semaphore 类中实现了公平锁和非公平锁,默认是非公平。和 ReentrantLock 类似。
三:Semaphore 类中的资源是使用了AQS中的state属性。
四: Semaphore 和CountDownLatch 的实现方式比较类似。大家可以比较学习。
CountDownLatch 学习可以参考:
https://blog.csdn.net/echohuangshihuxue/article/details/129280219
大家有什么补充的,随时欢迎。

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

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

相关文章

vue2、vue3组件传值,引用类型,对象数组如何处理

vue2、vue3组件传值&#xff0c;引用类型&#xff0c;对象数组如何处理 Excerpt 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定&#xff1a;父级 prop 的更新会向下流动到子组件中&#xff0c;但是反过来则不行。这样会防止从子组件意外变更父… 下述组件传值指引…

【Go|第1期】Go遍历目录的三种方法

日期&#xff1a;2023年3月1日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xff…

web,h5海康视频接入监控视频流记录一

项目需求&#xff0c;web端实现海康监控视频对接接入&#xff0c;需实现实时预览&#xff0c;云台功能&#xff0c;回放功能。 web端要播放视频&#xff0c;有三种方式&#xff0c;一种是装浏览器装插件&#xff0c;一种是装客户端exe&#xff0c;还有就是无插件了。浏览器装插…

垃圾回收的概念与算法(第四章)

《实战Java虚拟机&#xff1a;JVM故障诊断与性能优化 (第2版)》 第4章 垃圾回收的概念与算法 目标&#xff1a; 了解什么是垃圾回收学习几种常用的垃圾回收算法掌握可触及性的概念理解 Stop-The-World&#xff08;STW&#xff09; 4.1. 认识垃圾回收 - 内存管理清洁工 垃圾…

vue keep-alive多层级路由支持

keep-alive使用 属性值 1.include - 字符串或正则表达式。只有名称匹配的组件会被缓存。 2.exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。 3.max - 数字。最多可以缓存多少组件实例。 注&#xff1a;匹配首先检查组件自身的 name 选项&#xff0c;如果 nam…

用ab压测工具搞垮目标网站

一、介绍ab 命令会创建很多的并发访问线程&#xff0c;模拟多个访问者同时对某一 URL 地址进行访问。它的测试目标是基于 URL 的&#xff0c;因此&#xff0c;既可以用来测试 Apache 的负载压力&#xff0c;也可以测试 nginx、lighthttp、tomcat、IIS 等其它 Web 服务器的压力。…

数据结构与算法——6.Comparable接口

这篇文章我们一起来看一下java中的Comparable接口 目录 1.学数据结构与算法的小套路 2.Comparable接口介绍 3.小结 1.学数据结构与算法的小套路 我们知道java是面向对象的&#xff0c;并且底层为我们封装了许多的方法。在java的开发工具包jdk中&#xff0c;已经给我们提供…

XGBoost简单介绍

1. 概述 XGBoost本身的核心是基于梯度提升树实现的集成算法&#xff0c;整体来说可以有三个核心部分&#xff1a;集成算法本身&#xff0c;用于集成的弱评估器&#xff0c;以及应用中的其他过程。 1.1 提升集成算法&#xff1a; XGBoost的基础是梯度提升算法&#xff0c;因此…

kali linux安装换源切换系统语言

安装 去官网 https://www.kali.org/ 找到自己合适的虚拟机版本&#xff0c;我们不要下载那个torrent&#xff0c;那个还要重新下载一遍 换源 sudo vim /etc/apt/sources.list 按 i 进入vim的编辑模式 用 # 把用来的注释掉&#xff0c;一定要去掉 在后面补上国内的源&#x…

【论文/写作】计算机论文写作全攻略总结

如果觉得我的分享有一定帮助&#xff0c;欢迎关注我的微信公众号 “码农的科研笔记”&#xff0c;了解更多我的算法和代码学习总结记录。或者点击链接扫码关注【论文/写作】计算机论文写作全攻略总结 机器翻译学术论⽂写作⽅法和技巧 https://nlp.csai.tsinghua.edu.cn/~ly/tal…

一篇搞定ShardingSphere-jdbc 实战

谈到分库分表中间件时&#xff0c;我们自然而然的会想到 ShardingSphere-JDBC 。这篇文章&#xff0c;我们聊聊 ShardingSphere-JDBC 相关知识点&#xff0c;并实战演示一番。1 ShardingSphere 生态Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c;它包含两大产…

操作系统权限提升(二十三)之Linux提权-通配符(ws)提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 操作系统权限提升(二十)之Linux提权-计划任务提权 操作系统权限提升(二十一)之Linux提权-环境变量劫持提权 操作系统权限提升(二十二)之Linux提权-SUDO滥用提权 利用通配符…

redis的集群方式

1.主从复制 主从复制原理&#xff1a; 从服务器连接主服务器&#xff0c;发送SYNC命令&#xff1b; 主服务器接收到SYNC命名后&#xff0c;开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令&#xff1b; 主服务器BGSAVE执行完后&#xff0c;向所有从服务…

阿里测试7年,薪资从7K到25K,我的成功值得每一个人借鉴

7年从业经验&#xff0c;这篇文章将汇集自动化测试所需知识&#xff0c;拒绝标题党&#xff0c;水文。让所有想学习提升技术的能从文中获取有价值的知识。 在这个吃技术的IT行业来说&#xff0c;我之前每天做的是最基础功能测试的工作&#xff0c;但是随着时间的消磨&#xff…

C++回顾(五)—— 构造函数和析构函数

5.1 构造和析构 5.1.1 构造函数 &#xff08;1&#xff09;定义 1&#xff09;C中的类可以定义与类名相同的特殊成员函数&#xff0c;这种与类名相同的成员函数叫做构造函数&#xff1b;2&#xff09;构造函数在定义时可以有参数&#xff1b;3&#xff09;没有任何返回类型的…

第十届蓝桥杯省赛——5最大降水量(纯填空,分析)

题目&#xff1a;试题 E: 最大降雨量本题总分&#xff1a;15 分【问题描述】由于沙之国长年干旱&#xff0c;法师小明准备施展自己的一个神秘法术来求雨。这个法术需要用到他手中的 49 张法术符&#xff0c;上面分别写着 1 至 49 这 49 个数字。法术一共持续 7 周&#xff0c;每…

二叉树——二叉树的最近公共祖先

二叉树的最近公共祖先 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一…

如何使用DDexec在Linux上隐蔽运行二进制文件

关于DDexec DDexec是一种能够在Linux上使用无文件技术和隐秘技术运行二进制文件的方法&#xff0c;它可以使用dd工具来将Shell替换为其他进程。 众所周知&#xff0c;在Linux上运行一个程序&#xff0c;则这个程序必须以一个文件的形式存在&#xff0c;而且必须能够通过文件系…

电脑没有回收站找回删除文件的2种方法

最近后台收到了这样的咨询&#xff1a;”在网吧上网&#xff0c;删除东西的时候不小心把我的文件给删除了&#xff0c;但是桌面上没有回收站&#xff0c;怎么才能找回我的文件&#xff1f;“——针对“电脑没有回收站删除的东西怎么恢复”这种疑问&#xff1f;不妨看看下面数据…

环境搭建02-Ubuntu16.04 安装CUDA和CUDNN、CUDA多版本替换

1、CUDA安装 &#xff08;1&#xff09;下载需要的CUDA版本 https://developer.nvidia.com/cuda-toolkit-archive &#xff08;2&#xff09;安装 sudo sh cuda_8.0.61_375.26_linux.run&#xff08;3&#xff09;添加环境 gedit ~/.bashrc在文件末尾添加&#xff1a; ex…