面试官:Semaphore在项目中有使用过吗?

米家大1匹空调PG10/N1A1选购指南:能效与硬件配置拆解 空调选购中,匹数、能效等级和核心硬件是决定制冷效果与耗电量的关键。理解“大1匹”对应的制冷量范围,以及新一级能效背后的APF全年能源消耗效率,是避开宣传话术的基础。电子膨胀阀与毛细管的差异直接影响变频空调的低温制热和高温制冷表现,而全直流变频压缩机则是省电的核心支撑。对于10-12平米卧室,选择匹数、能效与硬件配置匹配的挂机,才能在长期使用中兼顾舒适与电费开支。本文以米家26GW-PG10/N1A1为例,拆解其APF 5.28、电子膨胀阀等参数,对比同价位竞品,梳理从选型、安装到省电使用的一整套实操方法,帮 阅读详情

Semaphore

它就是我们之前在讲源码的时候提到的 信号量 ,下面看下它的构造函数。

public Semaphore(int permits) {
        sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

从构造函数可以看出,它可以传入指定数量的资源和指定公平和非公平锁,公平和非公平就不多阐述了。

我们重点关注的是acquire()和release(), 这两个方法字面意思很好理解, Semaphore往往用于资源有限的场景,比如我们需要限制某个操作的线程数量。下面通过例子感受一下。

public class SemaphoreTest {
    public static final class Task implements Runnable {
        private int num;
        private Semaphore semaphore;
        public Task(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }
        @Override
        public void run() {
            try {
                // 获取
                semaphore.acquire();
                System.out.println(String.format("num: %d, 剩余%d个资源, 还有%d个线程在等待", num, semaphore.availablePermits(), semaphore.getQueueLength()));
                System.out.println(System.currentTimeMillis());
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 释放
                System.out.println("释放资源");
                semaphore.release();
            }
        }
    }
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);
        IntStream.range(0, 20).forEach(i -> new Thread(new Task(i, semaphore)).start());
    }
}

实际输出:

num: 1, 剩余0个资源, 还有0个线程在等待
1657591518171
num: 0, 剩余1个资源, 还有0个线程在等待
1657591518172
释放资源
....
释放资源
num: 18, 剩余0个资源, 还有1个线程在等待
1657591545235
num: 19, 剩余0个资源, 还有0个线程在等待
1657591545236
释放资源
释放资源
进程已结束,退出代码0

源码剖析

我们重点看下acquire()源码实现。

从这个信号量获取一个许可,阻塞直到有一个可用,或者线程被中断。获得一个许可,如果一个可用并立即返回,将可用许可的数量减少一个。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

重点是这个sync。

// 首先它继承 AbstractQueuedSynchronizer 这个大家肯定不陌生了 就是AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;
    // 初始化的时候会写入一个状态
    Sync(int permits) {
        setState(permits);
    }
    // 获取当前的状态
    final int getPermits() {
        return getState();
    }
    // 非公平方式获取信号量
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            // 当前可获取的
            int available = getState();
            // 计算剩余数量
            int remaining = available - acquires;
            // 如果剩余数量大于0 就是进行cas修改
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
    // 释放信号量
    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            // 释放后剩余的数量
            int next = current + releases;
            // 如果超出就
            if (next < current) // overflow
                throw new Error("Maximum permit count exceeded");
            if (compareAndSetState(current, next))
                return true;
        }
    }
    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            // 超出最大限量抛异常
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }
    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}

在构造函数中FairSync和NonfairSync他们都继承Sync。

sync = fair ? new FairSync(permits) : new NonfairSync(permits);

默认情况下非公平的Semaphore会去调用Sync的nonfairTryAcquireShared。

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

        NonfairSync(int permits) {
            super(permits);
        }
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

公平的Semaphore内部实现了tryAcquireShared()。

static final class FairSync extends Sync {
    private static final long serialVersionUID = 2014338818796000944L;
    FairSync(int permits) {
        super(permits);
    }
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
}

下面我们再回过头看下acquire(), 内部方法acquireSharedInterruptibly是AQS的内部方法。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

如果线程中断,直接抛异常, 如果没拿到资源就进入排队机制。

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
        // 可获取的资源数小于0进入排队 这里的实现在子类,就是上边提到的
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

重点看下这个doAcquireSharedInterruptibly()。

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    // 以共享模式加入到阻塞队列 之前讲源码的时候都讲过     
    final Node node = addWaiter(Node.SHARED);
    // 默认失败
    boolean failed = true;
    try {
        for (;;) {
            // 获取前置节点
            final Node p = node.predecessor();
            // 如果上一个节点就是头部节点 再次尝试获取 (原因是头部节点可能释放资源了)
            if (p == head) {
                int r = tryAcquireShared(arg);
                // 如果获取到了 并且还有剩余资源
                if (r >= 0) {
                    // 1. 将当前节点设置为头部节点
                    // 2. 判断后续节点是否是共享等待节点
                    // 3. 唤醒后续的节点
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            // 这一步主要是检查未能获取到资源的节点状态
            // 如果线程需要阻塞返回true
            // parkAndCheckInterrupt 如果线程中断了 抛出异常
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        // 如果失败 取消正在进行的获取
        if (failed)
            cancelAcquire(node);
    }
}

shouldParkAfterFailedAcquire()的细节我们也来看下,可能有的同学不大清楚。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 只要前置节点释放锁,就会通知标识为SIGNAL(-1)状态的后续节点的线程
        // 如果前置节点为SIGNAL,只需要等待其他前置节点的线程被释放,
        if (ws == Node.SIGNAL)
            return true;
        // 这里的判断指的是取消状态, 如果取消了就讲这个节点移除掉
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // cas 更新
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

这里的SIGNAL一类的常量,大家可以自行到源码查看,这也是细节地方。发现这段代码主要的作用就是检查节点状态,对后续节点做一些操作,这里并没有阻塞操作,下面我们看下parkAndCheckInterrupt()。

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

这里我们可以看到加了锁,所以阻塞发生在这。那么释放锁在哪呢?其实在release阶段。

private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

可以看到在unparkSuccessor中进行了锁的释放,这个过程发生在释放阶段。

release()相对简单一些,大家可以自己对着源码看下,实现有些类似。

结束语

其实本节带大家看源码,主要是想给大家讲下共享锁的知识,Semaphore其实就是使用了共享锁。另外AQS这个类很值得大家好好研究一下,你会发现很多的好用的类都是基于它实现。

 

Semaphore原理浅析和相关面试题分析 本文首发在个人公众号:HelloWorldEE,欢迎关注。 本篇文章的来源是这样,有一天,我一同学面试某公司回来,和我分享其被问的相关面试题。其中就有一道关于Semaphore的面试题,个人觉得比较经典,分享出来供大家参考。 具体同学和面试官的对话还原出来是这样。 面试官:现在有一个方法task,希望只能被10个线程调用,利用Java相关类,应该如何来实现? 同学:使用Java中的Semaphor... 阅读详情

相关推荐

看完这篇,面试官问 Semaphore,我能吹半小时!

嗨!大家好,我是你们熟悉的 31 岁程序员小米,一个喜欢写代码、讲故事、做饭、撸猫、写公众号的人。今天这篇文章,咱们继续讲点面试中高频但是常被忽视的 Java 多线程知识点。最近我一个老同事去面试,HR 问了个看似简单但实际很多人讲不清楚的问题:老同事回来跟我吐槽:“小米啊,我平时用 synchronized、ReentrantLock 用得顺溜,轮到 Semaphore 就懵圈,面试时候只说出是‘限流’用的,面试官明显不满意。”好家伙,咱这不行啊。既然如此,今天我就跟大家唠个透彻,顺便给正在准备社招的小伙

孤芳不自赏 396

Java面试题13-CountDownLatch和Semaphore的区别和底层原理

CountDownLatch与Semaphore的区别与原理

z318913的博客 540

Zynq UltraScale+ MPSoC-AMP(linux+裸机)

接着 Zynq UltraScale+ MPSoC-双核裸机AMP继续 平台工具:zcu106,vitis2020.2,petalinux2019.2 文章目录1.cpu1跑裸机2.cpu0跑linux2.1petalinux设置2.2 app3.生成BOOT.bin4.zcu106测试 1.cpu1跑裸机 参照Zynq UltraScale+ MPSoC-双核裸机AMP建立的工程进行修改。BSP中stdin与out修改为uart1,uart1给裸机用,uart0给linux用。 ddr空间裸机与linu

zhuxl24的博客 5196

JUC面试(七)——CountDownLatch&CyclicBarrier&Semaphore

JUC包工具类CountDownLatch&CyclicBarrier&Semaphore。

qq_43409401的博客 1550

统信UOS服务器上,达梦DM8数据库安装避坑全记录(附大小写敏感设置)

本文详细介绍了在统信UOS服务器版上部署达梦DM8数据库的全过程,包括环境准备、安装流程、数据库实例初始化及关键参数配置,特别是大小写敏感设置的注意事项。提供了从系统配置到性能优化的完整指南,帮助用户避免常见问题,确保数据库稳定运行。

weixin_30647423的博客 333

Vivado时序违例debug

显示了数据路径延迟。例如,对于违例路径,若布线延迟超过50%,说明该延迟是导致时序不能收敛的主要因素,因此可围绕降低布线延迟这一靶心达到时序收敛。对于跨时钟域路径,Requirement数值会有所变化,但如果数值为0.1,显然是不合理的。目的时钟路径单元,时钟从adc0_bufg/O输出,到达PLLE4_ADV,经过BUFGCE输出,最终到达SRLC32E移位寄存器,对应。源时钟路径单元,时钟从adc0_bufg/O输出,到达PLLE4_ADV,经过BUFGCE输出,最终到达FDRE触发器,对应。

weixin_44513618的博客 1622

2024大厂Java面试必问题目,并发工具之Semaphore与Exchanger,2024年Java进阶者的新篇章

至此,文章终于到了尾声。技术能力:先写岗位所需能力,再写加分能力,不要写无关能力;项目经历:只写明星项目,描述遵循 STAR 法则;简历印象:简历遵循三大原则:清晰,简短,必要,要有的放矢,不要海投;以及最后为大家准备的福利时间:简历模板+Java面试题+热门技术系列教程视频一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

2401_84102892的博客 1046

可编程逻辑控制器(PLC)系列:Mitsubishi MELSEC-Q_(8).MELSEC-Q系列PLC故障诊断与维护

MELSEC-Q系列PLC的故障诊断与维护是一个系统性的过程,需要工程师具备扎实的理论基础和丰富的实践经验。通过了解故障类型、使用诊断工具、遵循诊断步骤,可以有效地定位和解决故障问题。同时,定期的检查与保养、备份与恢复、优化程序和配置,以及提供良好的运行环境,可以减少故障的发生,提高系统的可靠性。希望本文能够帮助工程师在故障诊断与维护过程中更加得心应手,确保MELSEC-Q系列PLC的稳定运行。

2401_87715305的博客 1248

java高并发高频面试题:Sempahore的使用场景与常见误区

吹个牛:我有一双“火眼金睛”,任你刷多少题,是否用过我一问便知。

中间件兴趣圈 1532

部署模型:Libtorch使用笔记【Pytorch的C++版本】【把用Pytorch训练好的模型打包保存为.pt格式,使用Libtorch去加载.pt格式模型,在C++工程中去用就好了】

A : Libtorch是pytorch的C++版本,现在的很多大型项目都是用C++写的,想使用训练好的模型,需要通过caffe等方式去集成,比较麻烦。这里pytorch官方提出了Libtorch,我们就可以把pytorch训练好的模型,打包起来,直接在C++工程中去用就好了,相比较caffe等,非常方便!虽然对于许多需要动态性和易迭代性的场景来说,Python是一种合适且首选的语言,但在同样的情况下,Python的这些特性恰恰是不利的。生产环境是一个需要低延迟和有严格部署要求的领域,一般选择C++。

u013250861的博客 2950

YOLO系列算法改进 | C2PSA改进篇 | 融合BinaryAttention二值化注意力 | 轻量化与效率双提升,适用于高分辨率图像检测与边缘部署场景 | CVPR 2026

YOLO系列算法改进 | C2PSA改进篇 | 融合BinaryAttention二值化注意力 | 轻量化与效率双提升,适用于高分辨率图像检测与边缘部署场景 | CVPR 2026 (以YOLO26最新目标检测模型为基础,利用C2PSA_BinaryAttention创新模块进行改进)

2502_91881744的博客 166

Semaphore的概念及基本用法

Semaphore这个英文单词的意思是信号灯,即发送信号的那种灯。Java并发工具包中的Semaphore类则是线程之间互相发送信号的工具。我们可以把Semaphore看作一个包含多个许可(permit)的集合,例如一个代表5个许可的Semaphore、6个许可的Semaphore等等(为便于表达,后文用字母P表示许可)。Semaphore上的acuqire操作申请P,而release操作则产生P,Semaphore可用于追踪可用资源的个数。 1. 在代码中的某个地方创建Semaphore(第2个构造参数

mxy_111的博客 8414

不会C# Winform用户界面设计?是因为没有掌握这些控件(建议收藏)

文章目录6.1 常用控件6.1.1 单选按钮(RadioButton)6.1.2 复选框(CheckBox)6.1.3 框架1. 面板控件Panel2. 分组框GroupBox3. TabControl控件6.1.4 应用实例6. 2 列表框和组合框6.2.1 列表框(ListBox)6.2.2 组合框(ComboBox )6.2.3 应用实例6.3 用户交互界面6.3.1 滚动条和进度条6.3.2 定时器6.3.3 菜单设计菜单设计概述MenuStrip和ContextMenuSt

智慧空间实验室 2922

头条面试居然跟我扯了半小时的Semaphore

一个长头发、穿着清爽的小姐姐,拿着一个崭新的Mac笔记本向我走来,看着来势汹汹,我心想着肯定是技术大佬吧!但是我也是一个才华横溢的人,稳住我们能赢。

万猫学社 7万+

ARM与PC共享文件方式【samba】移植与配置

方式推荐指数适用平台优点缺点⭐⭐⭐⭐⭐全平台 (Win/Mac/Linux)兼容性最强,操作像本地文件配置稍繁琐,协议开销略大NFS⭐⭐⭐⭐Linux/Mac (Win支持一般)速度极快,CPU占用低Windows配置复杂,权限管理较难SFTP (SSH)⭐⭐⭐⭐全平台 (需客户端)安全加密,无需额外配置服务需使用FileZilla/Xftp等专用软件物理U盘⭐⭐⭐全平台无需网络,速度取决于接口需要人工插拔,不适合自动化Syncthing⭐⭐⭐⭐全平台自动同步,支持远程。

浩瀚之水的专栏 263

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

面试官:你来说说什么是Semaphore?

面试官:前面说到了CyclicBarrier 和CountDownLatch,你在说说Semaphore,你对它了解有多少? 初识Semaphore:        Semaphore 信号量,用来控制同一时间,资源可被访问的线程数量,一般可用于流量的控制。        Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有

东哥_Jeffery的博客 340

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

面试官:说说CountDownLatch,CyclicBarrier,Semaphore的原理?

CountDownLatch CountDownLatch适用于在多线程的场景需要等待所有子线程全部执行完毕之后再做操作的场景。 举个例子,早上部门开会,有人在上厕所,这时候需要等待所有人从厕所回来之后才能开始会议。 public class CountDownLatchTest { private static int num = 3; private static CountDownLatch countDownLatch = new CountDownLatch(num); p

艾小仙的专栏 534

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

面试官:说说Java中的信号量?Semaphore

Semaphore (信号量)是由计算机科学家Dijkstra在1965年提出的,广泛应用不同的操作系统,在管程提出之前信号量就是并发编程领域的霸主!几乎所有并发的语言都支持信号量机制。 Semaphore 也有被翻译成信号灯,因为其机制就像我们日常生活中的红绿灯,车辆的通行看红绿灯,对应编程世界的线程能不能执行得看信号灯! Semaphore 用来多线程互斥问题,相对于synchronized和...

weixin_34068198的博客 1252

【多线程并发编程】十四 面试官:说说Java中的信号量?Semaphore

面试经常会问java的信号量,本文通过概念,应用场景,api文档,源码,更方便理解信号量。

程序猿学社的博客 5407

java semaphore(0)_面试官:说说Java中的信号量?Semaphore

Semaphore (信号量)是由计算机科学家Dijkstra在1965年提出的,广泛应用不同的操作系统,在管程提出之前信号量就是并发编程领域的霸主!几乎所有并发的语言都支持信号量机制。Semaphore 也有被翻译成信号灯,因为其机制就像我们日常生活中的红绿灯,车辆的通行看红绿灯,对应编程世界的线程能不能执行得看信号灯!Semaphore 用来多线程互斥问题,相对于synchronized和Lo...

weixin_39567046的博客 311

头条面试居然跟我扯了半小时的Semaphore,好友给我分享了一份面试官最爱问的Java面试题

由于篇幅原因,就不多做展示了)][外链图片转存中…(img-nrb4wdpR-1712021196980)][外链图片转存中…(img-SJAAXS0o-1712021196981)][外链图片转存中…(img-si1Trwdy-1712021196981)][外链图片转存中…(img-XeGNdbM8-1712021196981)][外链图片转存中…(img-TuK4biIf-1712021196982)][外链图片转存中…(img-R0YK2BAl-1712021196982)]

2401_83974173的博客 837

没听说过CyclicBarrier、Phaser、Semaphore?面试官说:你可能没学过Java

Java 中的类非常非常多,虽然你不一定都用过,但是,对于一些关于线程交互、同步、通信等等相关的类我们还是需要去学习和了解的。 比如大名鼎鼎的 AQS,Lock…等等。 在我这一篇博客中,我主要去讲一些平时大家可能会忽略的一些但是又不能不知道的一些并发工具类,我会从主要的使用、原理,和一部分底层的代码来帮助大家理解。 前半部分讲解使用,后半部分分析源码 (源码是有关 AQS 的知识的,如果不懂 A...

小龙JWY的博客 608

面试官:说下CountDownLatch、CyclicBarrier、Semaphore同步器原理

CountDownLatch,闭锁,就是一个基于 AQS 共享模式的同步计数器,它内部的方法都是围绕 AQS 实现的。主要作用是使一个或一组线程在其他线程执行完毕之前,一直处于等待状态,直到其他线程执行完成后再继续执行。

Java后端架构V 269
上一篇: 浅析 Apache Kafka 分区重分配的实现原理
下一篇: 互联网公司都怎么实现分页的,拿 MySQL 使劲Limit?
程序员职业指南
博客等级 码龄4年 2138粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值