JAVA并发编程系列之Semaphore信号量剖析

news2024/9/22 15:18:51

腾讯T2面试,现场限时3分钟+限最多20行代码,模拟地铁口安检进站。其中安检入口10个,当前排队人数是100个,每个人安检进站耗时5秒。开始吧!

候选人,心中万马奔腾!!!吐了一口82年老血,当场砸电脑回家!

其实,面对这样的面试要求,现实中的头部大厂,甚至一些普通大厂都是设计了很多编程题考查大家的基础功底。但是都不会很复杂,毕竟时间有限,往往都是经典题目,涉及一个或多个核心关键技术点。

这个题目考察的就是并发编程,多个线程并发执行,但是共享资源有限,需要阻塞等待,或者自旋竞争锁。其实如果不限制代码行数,我们有非常多的方式去实现。

1、面试真题:模拟地铁站安检排队进站

这里我们用本文主角semaphore信号量去实现。先上代码,加上package 、import,刚好20行代码。

package lading.java.mutithread;
import cn.hutool.core.date.DateTime;
import java.util.concurrent.Semaphore;
/**
 * 模拟地铁安检入口排队进站
 * 共10个安检口
 * 当前有100人进站
 * 每人进站需要5s
 */
public class Demo008Semaphore {
    public static Semaphore doorNum = new Semaphore(10);//总安检口
    public static int peopleNum = 100;//当前排队进站人数
    public static int perPersonTimeCostSec = 5;//每个人进站耗时:S
    public static void main(String[] args) {
        for (int i = 1; i < peopleNum + 1; i++) {
            new Thread(() -> {
                try {
                    doorNum.acquire();
                    Thread.sleep(perPersonTimeCostSec * 1000);
                    System.out.println(DateTime.now().toString("YYYY-MM-dd hh:mm:ss") + " " + Thread.currentThread().getName() + " 完成进闸。");
                    doorNum.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "卡号" + i).start();
        }
    }
}

运行结果,刚好是每次10个人进站,5s后,又有10个人进站。

实现逻辑:每次只有10个人可以安检进站,进站前通过信号量去竞争锁,拿到就休眠5s,模拟进站耗时,然后释放锁,下一个人就可以继续竞争锁并进站

2、Semaphore信号量是什么?

首先Semaphore是JUC包提供的一个并发工具类,功能是:支持以及限制多个线程同时访问共享资源。之前我们说《synchronized全能王的原理》和可重入锁《ReentrantLock核心原理剖析》都是限制仅允许一个线程访问共享资源,确保并发的原子性、有序性、可见性。但是Semaphore信号量,像个限流器一样,允许N个线程同时执行。

我们看一下他的源码:

发现和之前分享的AQS优秀实践者ReentrankLock可重入锁,简直就是双胞胎兄弟,就差名字不一样了。里面的三个内部类名字完全一样,抽象类Sync,实现Sync的FairSync 类和NoFairSync类。

但是他是个非重入锁。内部就是通过设置volatile int state的值来维护许可令牌。当state值为0 的时候,其他未执行的线程只能阻塞等着。当有获得锁的线程执行完后,他会把state值+1,这样就相当于有一个空闲令牌,其他等待令牌的就可以竞争执行。

3、具体说一下对Semaphore实现原理

在2的源码图我们看到,信号量里面有三个内部类,其中Sync是直接实现了AQS AbstractQueueSynchronizer队列同步器。然后实现公平锁的FairSync 类和非公平锁NoFairSync类有是Sync的子类。所以信号量的核心在于公平锁、非公平锁的实现上。

首先说说,信号量获取锁的逻辑。这个和之前《ReentrantLock核心原理剖析》锁的公平锁、非公平锁逻辑非常像,这里我们也是上核心源码来剖析。

Semaphore permit= new Semaphore(3,true);
permit.acquire();

我们继续看获取锁的acquire()的源码.

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

这个acquireSharedInterruptibly方法是在AQS实现的,之前说AQS是模板方式的设计,这里子类就可以复用父类框架。

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

到正主了,tryAcquireShared(arg),这个方法里才是获取锁的核心逻辑。我们再继续往下看

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

        FairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
        //自旋
            for (;;) {
            // 1、首先判断 如果AQS FIFO队列是否有在等待的线程,如果有就返回获取锁失败
                if (hasQueuedPredecessors())
                    return -1;
                //如果步骤1当前队列是空,以及自己是队列的头节点==说明当前没有其他在等待更久竞争的线程
                int available = getState();
                //设置state,判断可用信号量是否大于0,大于0则获取锁成功,并通过CAS去更新state值
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }

刚看的是公平锁的源码逻辑,我们再简单看一下非公平锁逻辑。非公平锁简单暴力,上来没有公平锁那个hasQueuedPredecessors()逻辑,不判断是否有其他线程在等待,上来就直接判断当前是否还有可用信号量,以及通过CAS去更新设置state值。CAS成功就拿到锁。

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

4、Semaphore如何释放锁

释放锁,分2步。

1、tryReleaseShared();获取当前信号量值,并通过CAS去+1,更新state值。

2、doReleaseShared();唤醒队列的线程。

步骤1源码,自旋判断并CAS设置state值。

        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;
            }
        }

今天就这样,明天我们继续分享CountDownLatch、Future、CyclicBarrier等其他内容。

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

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

相关文章

re题(37)BUUCTF-[GWCTF 2019]xxor

BUUCTF在线评测 (buuoj.cn) 用ida打开文件&#xff0c;ctrle定位main函数 也可以用shiftF12查找字符串&#xff0c;找与我们解题有关的字符串 通过字符串定位到引用字符串的函数 进入main entry 但还不是我们要分析的代码 进入__libc_start_main中的main参数&#xff0c;是我们…

C++20 std::format

一、前言 1、传统 C 格式化的问题与挑战 可读性差&#xff1a;使用 C 中的 printf 和 scanf 家族函数进行格式化输出和输入时&#xff0c;它们的语法较为复杂&#xff0c;难以阅读。在较大的代码项目中&#xff0c;可读性差会导致维护困难。类型安全性差&#xff1a;printf 和…

IS-ISv4/6双栈

文章目录 IS-ISv4/6双栈实验要求配置 IS-ISv4/6双栈 实验要求 配置双栈 R1、2、3、4配置 IS-ISv4 和 IS-ISv6&#xff0c;配置IPv6多拓扑 上面为Level-1类型、中间为Level-1-2、下面是Level-2类型 还有就是说ATT位置1有一定要求连接L1/2连接L1或者L2类型路由器&#xff0c;至…

java23发布啦

2024年9月java23发布啦&#xff01;&#xff01;! JDK 23 提供了12 项增强功能&#xff0c;这些功能足以保证其自己的JDK 增强提案 - JEP &#xff0c;其中包括 8 项预览功能和 1 项孵化器功能。它们涵盖了对 Java 语言、API、性能和 JDK 中包含的工具的改进。除了 Java 平台上…

KVM环境下制作ubuntu qcow2格式镜像

如果是Ubuntu KVM环境是VMware虚拟机&#xff0c;需要CPU开启虚拟化 1、配置镜像源 wget -O /etc/apt/sources.list https://www.qingtongqing.cc/ubuntu/sources.list2、安装kvm qemu-img libvirt kvm虚拟化所需环境组件 apt -y install qemu-kvm virt-manager libvirt-da…

安装黑群晖,并使用NAS公网助手实现DDNS动态域名解析

很多人都会安装安装一个黑群晖进行练手&#xff0c;黑群晖有很多玩法和NAS套件&#xff0c;而且黑群晖安装比较简单&#xff0c;没有复杂的步骤&#xff0c;这也是很多人玩黑裙的理由&#xff0c;这里教大家如何安装黑群晖&#xff0c;并且安装神卓互联NAS公网助手实现DDNS动态…

arthas -- xxljob本地调试

方案一&#xff1a;测试类 package cn.wanda.wic.content.job.xxljob;import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;SpringBootTest public class ShopResourceMigrationJobTest {Reso…

C\C++内存管理详解

本次内容大纲&#xff1a; 1.C/C内存分布 大家看看下面的代码 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";char* pChar3 "abcd";int…

本地快速部署一个简洁美观的个人Halo博客网站并发布公网远程访问

文章目录 前言1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可参考已安装Docker步骤&#xff1a;1.2 在Docker中部署Halo 2. Linux安装Cpolar2.1 打开服务器防火墙2.2 安装cpolar内网穿透 3. 配置Halo个人博客公网地址4. 固定Halo公网地址 前言 本文主要介绍如何在Cen…

【Python日记】好丝滑的贪吃蛇

文章目录 原文&#xff1a;https://blog.c12th.cn/archives/39.htmlPython 贪吃蛇(pygame)前言部分代码完整代码 原文&#xff1a;https://blog.c12th.cn/archives/39.html Python 贪吃蛇(pygame) 前言 源代码参考B站: BV1cs411T7wW 效果展示 部分代码 框架 # 初始化 pygam…

基于微信小程序校园订餐的设计与开发+ssm(lw+演示+源码+运行)

摘 要 人民生活水平的提高就会造成生活节奏越来越快&#xff0c;很多人吃饭都采用点外卖的方式。现在点外卖的平台已有很多&#xff0c;大多都需要安装它们的APP才可以使用&#xff0c;并且没有针对校园。如果一味的使用外卖平台不仅会造成商家成本的增加&#xff0c;还不利于…

django应用JWT(JSON Web Token)实战

文章目录 一、什么是JWT二、为什么使用JWT三、在django项目中如何应用JWT1、安装djangorestframework-simplejwt库&#xff1a;2、在settings.py中配置JWT认证&#xff1a;3、在urls.py中配置JWT的获取和刷新路由&#xff1a; 四、JWT如何使用1、调用生成JWT的接口获取JWT2、客…

C++:类型转换(static_cast、reinterpret_cast、const_cast、dynamic_cast)、RTTI

C&#xff1a;类型转换&#xff08;static_cast、reinterpret_cast、const_cast、dynamic_cast&#xff09;、RTTI 一、C语言类型转换二、C新增强制类型转换2.1 新增类型转换&#xff1a;static_cast2.2 新增类型转换&#xff1a; reinterpret_cast2.3 新增类型转换&#xff1a…

新手上路:在Windows CPU上安装Anaconda和PyCharm

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一. 确认自己是CPU为什么选择CPU教程&#xff1f;GPU与CPU的区别如何判断自己是CPU 二. Anaconda 安装包 和 Pycharm 安装包步骤1&#xff1a;下载Anaconda步骤2&am…

【d46】【Java】【力扣】234.回文链表

思路 判断是否是回文&#xff0c;需要&#xff1a;一个指针指向头&#xff0c;一个指针指向尾&#xff0c;两个指针一边向中间靠拢&#xff0c;一边判断数值是否相同 对于单链表&#xff0c;不方便获得pre&#xff0c;如果将节点放进 数组/list &#xff0c;数组/list可以直接…

2024年9月22日历史上的今天大事件早读

189年9月22日 &#xff08;汉灵帝中平六年八月戊辰&#xff09;大将军何进入宫&#xff0c;策划铲除宦官&#xff0c;事败被杀 1499年9月22日 瑞士独立。 1630年9月22日 明朝抗清将领袁崇焕被崇祯皇帝凌迟处死于北京 1776年9月22日 美国间谍黑尔未经审判即被处绞刑 1860年9…

buucft hashcat

使用文本编辑器打开时乱码 使用010editor打开发现时xml文档 拷贝到kali&#xff0c;使用binwalk查看&#xff0c;发现时xml文档&#xff0c;改后缀名为ppt。打开发现有密码 Accent OFFICE Password Recovery 64位-Office密码恢复软件 v20.09 免费版 - 下载吧 试试这个Accent O…

[云服务器14] 搭建属于你自己的Git服务器

Git是由Linux Torvalds开发的一个版本控制系统(VCS,Version Control System)&#xff0c;而目前最大的Git仓库托管公司就是大名鼎鼎的GitHub啦&#xff01; 而这次&#xff0c;我们主要讲在自己的服务器上&#xff0c;搭建一个GitLab驱动的Git服务器&#xff01; [请CSDN审核…

大模型的威力,远不只是聊天框

自2022年底ChatGPT发布以来&#xff0c;我们真正看到了大模型的威力&#xff0c;也引发了技术圈种种畅想。有相当一部分观点认为&#xff0c;大模型是一种工业革命级的产物&#xff0c;相信更多的人会和笔者一样&#xff0c;认为大模型是一个操作系统级的产品&#xff0c;会彻底…

基于波特图的控制系统设计算法

波特图&#xff08;Bode Plot&#xff09;是一种用于描述线性控制系统频率响应的图形表示方法&#xff0c;通常用于分析和设计控制系统。它以控制系统的传递函数&#xff08;或频域传递函数&#xff09;为基础&#xff0c;将系统的幅频特性&#xff08;振幅-频率响应&#xff0…