Semaphore 使用及原理

信号量Semaphore详解 大家应该都用过关键字加锁,用来保证某个时刻只允许一个线程运行。那么如果控制某个时刻允许指定数量的线程执行,有什么好的办法呢?答案就是JUC提供的信号量Semaphore。Semaphore(信号量)可以用来限制能同时访问共享资源的线程上限,它内部维护了一个许可的变量,也就是线程许可的数量Semaphore的许可数量如果小于0个,就会阻塞获取,直到有线程释放许可Semaphore是一个非重入锁构造方法permits表示许可线程的数量fair表示公平性,如果设为true。 阅读详情

1、Semaphore 是什么

Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。

2、使用场景

朱勇用于那些资源有明确访问数量限制的场景,常用于限流 。

比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。

比如:停车场场景,车位数量有限,同时只能容纳多少台车,车位满了之后只有等里面的车离开停车场外面的车才可以进入。

3、Semaphore常用方法说明

信号量的构造函数
非公平:

public Semaphore(int permits);//permits就是允许同时运行的线程数目

公平(获得锁的顺序与线程启动顺序有关):

public Semaphore(int permits,boolean fair);//permits就是允许同时运行的线程数目

acquire()  
获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
​
acquire(int permits)  
获取一个令牌,在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。
    
acquireUninterruptibly() 
获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。
    
tryAcquire()
尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
​
tryAcquire(long timeout, TimeUnit unit)
尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。
​
release()
释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。
​
hasQueuedThreads()
等待队列里是否还存在等待线程。
​
getQueueLength()
获取等待队列里阻塞的线程数。
​
drainPermits()
清空令牌把可用令牌数置为0,返回清空令牌的数量。
​
availablePermits()
返回可用的令牌数量。

4、用semaphore 实现停车场提示牌功能。

每个停车场入口都有一个提示牌,上面显示着停车场的剩余车位还有多少,当剩余车位为0时,不允许车辆进入停车场,直到停车场里面有车离开停车场,这时提示牌上会显示新的剩余车位数。

业务场景 :

1、停车场容纳总停车量10。

2、当一辆车进入停车场后,显示牌的剩余车位数响应的减1.

3、每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。

4、停车场剩余车位不足时,车辆只能在外面等待。

代码:

public class TestCar {
​
    //停车场同时容纳的车辆10
    private  static  Semaphore semaphore=new Semaphore(10);
​
    public static void main(String[] args) {
​
        //模拟100辆车进入停车场
        for(int i=0;i<100;i++){
​
            Thread thread=new Thread(new Runnable() {
                public void run() {
                    try {
                        System.out.println("===="+Thread.currentThread().getName()+"来到停车场");
                        if(semaphore.availablePermits()==0){
                            System.out.println("车位不足,请耐心等待");
                        }
                        semaphore.acquire();//获取令牌尝试进入停车场
                        System.out.println(Thread.currentThread().getName()+"成功进入停车场");
                        Thread.sleep(new Random().nextInt(10000));//模拟车辆在停车场停留的时间
                        System.out.println(Thread.currentThread().getName()+"驶出停车场");
                        semaphore.release();//释放令牌,腾出停车场车位
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },i+"号车");
​
            thread.start();
​
        }
​
    }
}
​

5、Semaphore实现原理

(1)、Semaphore初始化。

Semaphore semaphore=new Semaphore(2);

1、当调用new Semaphore(2) 方法时,默认会创建一个非公平的锁的同步阻塞队列。

2、把初始令牌数量赋值给同步队列的state状态,state的值就代表当前所剩余的令牌数量。

初始化完成后同步队列信息如下图:

(2)获取令牌

semaphore.acquire();

1、当前线程会尝试去同步队列获取一个令牌,获取令牌的过程也就是使用原子的操作去修改同步队列的state ,获取一个令牌则修改为state=state-1。

2、 当计算出来的state<0,则代表令牌数量不足,此时会创建一个Node节点加入阻塞队列,挂起当前线程。

3、当计算出来的state>=0,则代表获取令牌成功。

源码:

/**
     *  获取1个令牌
     */
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

/**
     * 共享模式下获取令牌,获取成功则返回,失败则加入阻塞队列,挂起线程
     * @param arg
     * @throws InterruptedException
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //尝试获取令牌,arg为获取令牌个数,当可用令牌数减当前令牌数结果小于0,则创建一个节点加入阻塞队列,挂起当前线程。
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

/**
     * 1、创建节点,加入阻塞队列,
     * 2、重双向链表的head,tail节点关系,清空无效节点
     * 3、挂起当前节点线程
     * @param arg
     * @throws InterruptedException
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //创建节点加入阻塞队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                //获得当前节点pre节点
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//返回锁的state
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //重组双向链表,清空无效节点,挂起当前线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

线程1、线程2、线程3、分别调用semaphore.acquire(),整个过程队列信息变化如下图:

(3)、释放令牌

 semaphore.release();

当调用semaphore.release() 方法时

1、线程会尝试释放一个令牌,释放令牌的过程也就是把同步队列的state修改为state=state+1的过程

2、释放令牌成功之后,同时会唤醒同步队列的所有阻塞节共享节点线程

3、被唤醒的节点会重新尝试去修改state=state-1 的操作,如果state>=0则获取令牌成功,否则重新进入阻塞队列,挂起线程。

源码:

 /**
     * 释放令牌
     */
    public void release() {
        sync.releaseShared(1);
    }

/**
     *释放共享锁,同时唤醒所有阻塞队列共享节点线程
     * @param arg
     * @return
     */
    public final boolean releaseShared(int arg) {
        //释放共享锁
        if (tryReleaseShared(arg)) {
            //唤醒所有共享节点线程
            doReleaseShared();
            return true;
        }
        return false;
    }

 /**
     * 唤醒所有共享节点线程
     */
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {//是否需要唤醒后继节点
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))//修改状态为初始0
                        continue;
                    unparkSuccessor(h);//唤醒h.nex节点线程
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE));
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

继上面的图,当我们线程1调用semaphore.release(); 时候整个流程如下图:

Semaphore的使用详解 Semaphore在初始化时需要指定许可证数,Semaphore#acquire方法会尝试获取一定数量的许可证,若许可证数量不足,则当前线程进入阻塞状态。相应地,获取到了许可证的线程,在执行完毕后,需要调用Semaphore#release方法来归还许可证。 阅读详情

相关推荐

使用信号量(Semaphore)实现生产者与消费者模型

什么是生产者与消费者模型? 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。 打个比方 好比去肯德基吃汉堡,货架上有做好的汉堡。 消费者花钱来消费汉堡,而肯德基的厨师则不停地做汉堡放在货架上。 当货架满了,却没有消费者再来买汉堡时,厨师就不做了,生产者阻塞。 当餐点来了,消费者买的太快,厨师做不过来了,货架一直空着。 则消费者排队等着,消费者阻塞。

阿呆的专栏 2298

Semaphore详解

判断是否满足获取锁条件,关键方法.若获取锁成功,则也会修改state.若获取锁失败,关键方法阻塞的获取锁.添加到双向链表若是头节点后继,则尝试获取锁,否者则判断进入睡眠等待唤醒,唤醒后继续执行3.2若不进入睡眠,则直接运行到3.2步释放N个许可,因为存在并发释放,需要CAS确保设置更新后的值.唤醒双向链表中有效的等待节点.(可能存在并发问题,引入PROPAGATE状态)被唤醒的节点调用获取锁的流程....

Admans的专栏 2万+

ArcGIS通过已知坐标点转面(通过excel文件,有工具可批量生成面要素)

通过使用ArcGIS中的工具,将坐标点XY数据转换成可视化的点线面矢量数据,更加方便快捷有效的获取对应信息

qq_41046162的博客 3万+

Semaphore 详解

可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。每个停车场入口都有一个提示牌,上面显示着停车场的剩余车位还有多少,当剩余车位为0时,不允许车辆进入停车场,直到停车场里面有车离开停车场,这时提示牌上会显示新的剩余车位数。3、每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。

qq_58148854的博客 2607

【电子元件篇】电容的九大作用与选型指南-超简单解读

本文系统介绍了电容的基础概念、核心功能及实际应用。电容作为存储电荷的被动元件,其容值公式为C=Q/V,单位包括法拉及常用派生单位。文章重点解析了电容的九大功能:滤波(通过RC低通电路抑制噪声)、耦合(传递交流隔离直流)、去耦(稳定供电)、储能(短暂供电)、定时(RC延时)、谐振(RLC振荡)、旁路(高频通路)、电压调节(DC-DC转换)以及保护(吸收浪涌)。同时详细阐述了电容分类(陶瓷/电解/薄膜等)、参数选型要点(耐压/容值/精度/温度)及PCB设计注意事项,并针对不同应用场景提供了具体选型建议和常见误区

2301_81299416的博客 4008

GAMES101作业3-遇到的各种问题及解决方法

记录在做作业3时遇到的一些坑!以及最后的解决办法。

qq_41835314的博客 6817

CyclicBarrier,CountDownLatch,Semaphore的实现原理,以及使用场景

适合多线程协调的阶段性同步,所有线程需要同时达到某个状态才继续执行,且可以重用。适合一次性等待其他线程完成某些操作,倒计时计数器达到 0 后,阻塞的线程才会被唤醒。Semaphore用于控制并发线程的数量,限制访问共享资源的线程数,适合限流或资源保护场景。

yymagicer的博客 1453

《大明王朝1566》中,朝廷财政危机无法解决的根源分析

财政困局中的财富最终流向三重黑洞:​皇室挥霍​(如嘉靖炼丹修殿)、特权阶层贪腐​(严党年入百万两盐税)、制度性损耗​(宗室供养、低效官僚)。更深层的“钱”其实是被禁锢在道德化治理的牢笼中——当国家用“黄河长江皆可为用”的权术思维替代制度性改革时,财政危机便成为帝制王朝无法逃脱的宿命。

为了小家奋斗 1968

Semaphore的使用及原理

前言 Semaphore也是JUC包中一个用于并发控制的工具类,举个常用场景的例子:有三台电脑五个人,每个人都要用电脑注册一个自己的账户,这时最开始只能同时有三个人操作电脑注册账户,这三个人中有人操作完了剩下的两个人才能占用电脑注册自己的账户。这就是Semaphore的经典使用场景,跟并发加锁有点像,只是我们的并发加锁同一时间只让有一个线程执行,而Semaphore的加锁控制是允许同一时...

gr32442187do的博客 385

Apple 网页 SRP 登录系列文章(四):Hashcash 工作量证明与完整登录流程

本文是Apple SRP登录系列文章的收官之作,重点解析了Apple独有的Hashcash工作量证明机制并展示了完整的登录流程实现。文章详细介绍了Hashcash的安全目的、格式标准、计算核心算法及难度评估方法,特别提供了Python实现代码。同时还讲解了如何动态获取服务器Hashcash参数以及建立容错机制。最后呈现了完整的Apple SRP登录主函数架构,为开发者提供了从理论到实践的全面指导。全文通过代码示例和流程分析,系统性地阐述了Apple安全登录机制的技术细节。

Sayid_Des的专栏 629

飞书日历同步Outlook翻车?我踩过的坑和最终用Google Calendar成功的完整配置记录

本文详细记录了从Outlook日历同步到飞书时遇到的常见问题及解决方案,特别是通过Google Calendar实现稳定同步的完整配置流程。文章揭示了Outlook同步方案易翻车的原因,并提供了Google Calendar的避坑指南、高级技巧和企业级部署建议,帮助用户高效实现日历同步。

weixin_33724046的博客 313

Semaphore 使用及原理详解

Semaphore 是一个强大的并发控制工具,它通过维护许可数量来控制对共享资源的访问。理解其基于 AQS 的实现原理有助于更好地使用它。在实际开发中,Semaphore 常用于限流、连接池管理、生产者-消费者模式等场景。

weixin_46322148的博客 1112

2026 企查查/天眼查:IP频率限制 + 验证码策略,企业工商数据采集实战

企业工商数据采集技术解析与解决方案 本文深度剖析了2026年企查查和天眼查平台的反爬机制与技术对抗方案。主要内容包括: 反爬机制分析: 采用JA4+ TLS指纹识别、极验v6验证码等多层防护 IP限制策略严格区分未登录/普通/VIP账号 精准识别并封禁数据中心IP 核心解决方案: 构建高可用住宅代理池,动态轮换真实用户IP 基于YOLOv8的验证码缺口识别技术(准确率99%) 分布式架构设计,支持千万级数据采集 多账号池管理与行为模拟技术 技术亮点: 动态住宅IP池实现1%以下封禁率 Playwright浏

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1326

Semaphore基本使用及原理

Semaphore基本使用及原理 文章目录Semaphore基本使用及原理1 什么是Semaphore2 基本使用3 源码分析3.1 构造方法3.2 获取许可3.2.1 `tryAcquire`方法3.2.2 `acquire`方法3.3 释放许可4 总结 在前面的文章中我们学习了AQS、ReentrantLock和CountDownLatch的原理。在今天的文章中我们再学习一个JUC下的工具类-Semaphore。 今天的文章中我们会先介绍下Semaphore的使用,然后通过源码来学习下其是如何实现的。

weixin_45275252的博客 777

论文阅读: 2024 ICLR AutoDAN: Generating Stealthy Jailbreak Prompts on Aligned Large Language Models

AutoDAN: Generating Stealthy Jailbreak Prompts on Aligned Large Language Models这篇论文名为“AUTODAN: GENERATING STEALTHY JAILBREAK PROMPTS ON ALIGNED LARGE LANGUAGE MODELS”,主要研究大语言模型(LLMs)的越狱攻击问题,提出了一种名为AutoDAN的新方法,能自动生成隐蔽的越狱提示,具体内容如下:

CSPhD-winston的博客 1177

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Semaphore 原理简介和使用

Semaphore共享锁 简介 在多线程环境下用于协调各个线程, 以保证它们能够正确、合理的使用公共资源 信号量维护了一个许可集,我们在初始化Semaphore时需要为这个许可集传入一个数量值, 该数量值代表同一时间能访问共享资源的线程数量。 线程可以通过acquire()方法获取到一个许可,然后对共享资源进行操作, 如果许可集已分配完了,那么线程将进入等待状态, 直到其他线程释放许可才有机会再获取许可,线程释放一个许可通过release()方法完成 DEMO了解其用法 上述示例说明:

孟凡霄 520

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Semaphore使用以及原理

Semaphore(信号量)是一种用于控制并发访问资源的机制。它可以用于多线程或多进程环境中,用于保护共享资源的访问,避免竞争条件和死锁。

weixin_44792849的博客 470

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Semaphore 使用&核心原理 图解

疯狂创客圈 经典图书 : 《Netty Zookeeper Redis 高并发实战》 面试必备 + 面试必备 + 面试必备 【博客园总入口 】 疯狂创客圈 经典图书 : 《SpringCloud、Nginx高并发核心编程》 大厂必备 + 大厂必备 + 大厂必备 【博客园总入口 】 入大厂+涨工资必备: 高并发【 亿级流量IM实战】 实战系列 【 SpringCloud Nginx秒杀】 实战系列 【博客园总入口 】 JUC 高并发工具类 3文章: 1 ..

架构师尼恩 882

Semaphore使用及原理解读

Semaphore(信号量)是Java中一个并发控制工具,用于控制对共享资源的访问。它基于计数器的原理,可以限制同时访问某个资源的线程数量。在Java中使用Semaphore,你需要按照以下步骤进行操作:导包:其中,n是允许同时访问共享资源的线程数量。try {// 获取信号量,如果没有可用的许可证,线程将被阻塞// 访问共享资源的代码// 处理中断异常// 释放信号量,增加一个许可证acquire()

一个菜鸡的博客 3680

Java信号量semaphore的原理与使用方法

如果没有可用的许可,线程就会被阻塞,直到有其他线程释放一个许可。相反,release()方法会增加许可的数量,并有可能唤醒等待的线程。通过限制可用的连接数量,Semaphore确保了不会有太多的线程同时访问数据库。线程可以通过acquire()方法来获取许可,如果没有可用的许可,该线程就会阻塞,直到有许可可用。理解了Semaphore的基础和原理后,咱们现在可以探索它在实际场景中的具体使用。它的核心就是维护了一个许可集。简单来说,就是有一定数量的许可,线程需要先获取到许可,才能执行,执行完毕后再释放许可。

zjshuster的博客 590

semaphore信号量使用以及原理

什么是信号量(semaphore) 是一种JDK内置同步器,通过他可以实现多线程对公共资源的并发访问请求 可以看成一个计数器,当计数器的值小于许可最大值时,所有acquire方法线程都可以得到一颗许可继续执行,而调用release方法则可以让计数器的值减一 常用的重要方法 1、public Semaphore(int permits) // 创建一个给定许可数量的信号量对象,且默认以非公...

white_zzZ的博客 489

Semaphore使用及原理

1、Semaphore 是什么 Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。 可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。 2、使用场景 朱勇用于那些资源有明确访问数量限制的场景,常用于限流 。 .

Mrs_haining的博客 418
上一篇: CountDownLatch原理以及使用案例
下一篇: CyclicBarrier原理
wrr-cat
博客等级 码龄9年 128粉丝 302原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值