首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >有我在就没有人能拷打你!一文火速讲清JUC中常问类Semaphore

有我在就没有人能拷打你!一文火速讲清JUC中常问类Semaphore

作者头像
程序员牛肉
发布2025-06-27 14:59:27
发布2025-06-27 14:59:27
9300
代码可运行
举报
运行总次数:0
代码可运行

大家好,我是程序员牛肉。

对于 Java 初学者而言,JUC是突破高并发编程的关键跳板。其中精妙的设计思想 —— 从锁优化到无锁并发,从线程协同到资源调度 —— 堪称并发编程的 "设计模式宝典"。

我正在挑战讲完 JUC 的所有源码点击关注,带你穿透 API 文档,从此不畏惧JUC拷打

今天我们来和大家一起读一下Semaphore。

大家经常把Semaphore叫做“信号量”,其用途我们可以直接看Doug lea留下来的注释:

[信号量(Semaphore)在概念上维护一组许可(permits)。每次调用 acquire() 方法时,若有必要会阻塞线程直到获得许可,然后占用该许可。每次调用 release() 方法会增加一个许可,可能唤醒阻塞的获取者。需要注意的是,Semaphore实际并无物理许可对象;Semaphore 仅通过计数器跟踪可用许可数量并据此操作]

那其实这里就有一个问题了,Semaphore看起来和Lock没什么区别。都是基于许可来确定是否要阻塞当前线程。那Doug lea为什么要创建出来两个逻辑差不多的类?

因为从设计思想上来讲,Lock是为了实现严格实现互斥访问(Mutual Exclusion),确保同一时刻仅有一个线程能访问共享资源(如临界区代码或数据)。

因此当Lock在释放凭证(permits)的时候,他会判断一下当前的线程是不是持有锁的线程:

而Semaphore的本质只是释放出一批通行证,只要你获取了就可以通行,因此Semaphore在释放凭证的时候,并不关注当前的线程是不是持有锁的线程:

因此如果我们做一个形象的比喻的话,我们可以将Semaphore看做是停车场中显示剩余车位的告知牌。

Semaphore并不管这是谁的车,Semaphore只关注当前的车位还够不够你停进去。

让我们从源码角度来认识一下Semaphore吧。Semaphore有一个继承于AQS的sync内部类,几乎所有的锁在实现的时候都用到了sync内部类。

Semaphore和其他锁一样支持公平锁和非公平锁。

而之所以能实现公平锁,是因为其依赖了AQS框架。当线程想要获取凭证的时候就要先进队列中进行排队,严格执行先到先得。

在这里面调用hasQueuedPredcessors方法判断了一下队列中是否还有比自己靠前的元素:

代码语言:javascript
代码运行次数:0
运行
复制
public final boolean hasQueuedPredecessors() {
    // h = 头节点, s = 待检查的候选节点
    Node h, s;
    
    // 1. 检查队列是否非空(head != null)
    if ((h = head) != null) {
        
        // 2. 检查头节点的直接后继节点(第一个等待节点)
        if ((s = h.next) == null || s.waitStatus > ) {
            // 情况:后继节点不存在或已取消(waitStatus > 0 表示 CANCELLED)
            s = null;  // 初始化s
            
            // 3. 从尾节点向前遍历查找有效节点
            for (Node p = tail; p != h && p != null; p = p.prev) {
                if (p.waitStatus <= )  // 跳过已取消的节点
                    s = p;  // 记录最后一个有效节点
            }
        }
        
        // 4. 判断找到的有效节点是否属于当前线程
        if (s != null && s.thread != Thread.currentThread())
            returntrue;  // 存在其他排队线程
    }
    // 5. 默认返回false(无线程排队或当前线程是第一个)
    returnfalse; 
}

而非公平锁的设计就很简单了,只需要写死一个for循环之后不断的尝试获取凭证就好了:

剩下的方法也没有什么好讲的了。当你回头看去JUC的大部分锁的时候,其实你就会发现这些锁其实内部没什么方法,大部分核心逻辑都交给AQS以及sync包去做了。

这种“控制反转”和“模板方法”的设计模式,在 JUC 包中体现得淋漓尽致,也是我们学习源码时最值得品味的地方。

那今天关于Semaphore的源码讲解就到这里了。相信通过我的介绍,你已经了解了Semaphore的设计思想。

对于Semaphore你还有什么想聊的吗?欢迎在评论区留言。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员牛肉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档