Java并发编程三大神器之Semaphore

news2024/11/23 19:03:50

Java并发编程三大神器之Semaphore

  • 1、Semaphore是什么
  • 2、Semaphore小试牛刀
  • 3、Semaphore和CountDownLatch组合使用
  • 4、Semaphore常用方法
  • 5、Semaphore 结语

1、Semaphore是什么

Semaphore 是一个计数信号量,是JDK1.5引入的一个并发工具类,位于java.util.concurrent包中。可以控制同时访问资源的线程个数。Semaphore机制是提供给线程抢占式获取许可,所以他可以实现公平或者非公平,类似于ReentrantLock。

《Java并发编程艺术》中说:Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

举个栗子:我们去停车场停车,停车场总共只有5个车位,但是现在有8辆汽车来停车,剩下的3辆汽车要么等其他汽车开走后进行停车,要么去其他停车场找别的停车位。

举个栗子:公园里面的厕所只有4个坑,假如有10个人要上厕所的话,那么同时能去上厕所的人就只能有4个,还剩下6个人只能在外面等待。当4个人中有任何一个人离开后,其中在等待的人中才有一个人可以继续使用,依次下去,直到所有人都上完厕所。

2、Semaphore小试牛刀

Semphore计数信号量由 new Semaphore(N) 指定数量N的 “许可” 初始化。每调用一次 acquire(),一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。因此,在没有任何 release() 调用时,最多有 N 个线程能够通过 acquire() 方法,N是该信号量初始化时的许可的指定数量。这些许可只是一个简单的计数器。

如下的例子,有一个商场拥有4个车位的停车场,有10辆车需要停车,当停进4辆车后,其余的6辆车就需要等待已经停进去的车开出去,才可以再停进去。

public class ParkingLot extends Thread {
    // 信号量
    private Semaphore semaphore;
    // 同时允许多少个线程同时执行
    private int num;

    public ParkingLot(Semaphore semaphore, int num) {
        this.semaphore = semaphore;
        this.num = num;
    }

    @Override
    public void run() {
        try {
            //汽车驶入停车场,需要获取一个许可
            semaphore.acquire();
            System.out.println(LocalDateTime.now() + " 第" + this.num + "辆汽车驶入停车场...");
            Thread.sleep(2000);
            System.out.println(LocalDateTime.now() + " 第" + this.num + "辆汽车驶出停车场>>>");//汽车驶出停车场,会释放一个许可
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ParkingLotSemaphoreDemo {
    // 停车场总共有5个车位
    private static final int MAX_AVAILABLE = 4;
    
    public static void main(String[] args) {
        //只有5个车位,就是说同时只能允许5辆汽车进行停车
        Semaphore semaphore = new Semaphore(MAX_AVAILABLE);
        //模拟10辆车驶入停车场
        for (int i = 1; i <= 10; i++) {
            new ParkingLot(semaphore, i).start();
        }
    }
}

在这里插入图片描述

3、Semaphore和CountDownLatch组合使用

比如说

public class SemaphoreTest {
    public static void main(String[] args) throws InterruptedException {
        // 初始化五个车位
        Semaphore semaphore = new Semaphore(5);
        // 等所有车子
        final CountDownLatch latch = new CountDownLatch(8);
        for (int i = 1; i <= 8; i++) {
            int finalI = i;
            if (i == 5) {
                Thread.sleep(1000);
                new Thread(() -> {
                    stopCarNotWait(semaphore, finalI);
                    latch.countDown();
                }).start();
                continue;
            }
            new Thread(() -> {
                stopCarWait(semaphore, finalI);
                latch.countDown();
            }).start();
        }
        latch.await();
        log("总共还剩:" + semaphore.availablePermits() + "个车位");
    }
	// 停车 等待车位
    private static void stopCarWait(Semaphore semaphore, int finalI) {
        String format = String.format("车牌号%d", finalI);
        try {
            semaphore.acquire(1);
            log(format + "找到车位了,去停车了");
            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            semaphore.release(1);
            log(format + "开走了");
        }
    }
	// 不等待车位 去其他地方停车 
    private static void stopCarNotWait(Semaphore semaphore, int finalI) {
        String format = String.format("车牌号%d", finalI);
        try {
            if (semaphore.tryAcquire()) {
                log(format + "找到车位了,去停车了");
                Thread.sleep(10000);
                log(format + "开走了");
                semaphore.release();
            } else {
                log(format + "没有停车位了,不在这里等了去其他地方停车去了");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
	// 日志
    public static void log(String content) {
        // 格式化
        DateTimeFormatter fmTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.format(fmTime) + "  " + content);
    }
}

在这里插入图片描述

从输出结果可以看到车牌号6这辆车一看没有车位了,就不在这个地方傻傻的等了,而是去其他地方了,但是车牌号7车牌号8分别需要等到车库开出2辆车空出2个车位后才停进去。这就体现了 Semaphoreacquire() 方法如果没有获取到凭证它就会阻塞,而 tryAcquire() 方法如果没有获取到凭证不会阻塞的。

4、Semaphore常用方法

public Semaphore(int permits):创建一个信号量,参数permits表示许可数目,即同时可以允许多少线程进行访问。默认采用的是非公平的策略。
public Semaphore(int permits, boolean fair):创建一个信号量,参数permits表示许可数目,即同时可以允许多少线程进行访问。多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可。

public void acquire():用于获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。【会阻塞】
public void acquire(int permits):用于获取permits个许可,若无许可能够获得,则会一直等待,直到获得许可。【会阻塞】

public void release():用于释放一个许可。在释放许可之前,得先获得许可。【会阻塞】
public void release(int permits):用于释放permits个许可。在释放许可之前,得先获得许可。【会阻塞】

public boolean tryAcquire():尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false。【不会阻塞】
public boolean tryAcquire(long timeout, TimeUnit unit):尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false。【不会阻塞】
public boolean tryAcquire(int permits):尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false。【不会阻塞】
public boolean tryAcquire(int permits, long timeout, TimeUnit unit):尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false。【不会阻塞】

public int availablePermits():用于获取信号量中当前可用的许可数目。

5、Semaphore 结语

  • 当信号量Semaphore初始化设置许可证为 1 时,它也可以当作互斥锁使用。其中 0、1 就相当于它的状态,当=1时 表示其他线程可以获取;当=0时 排他,即其他线程必须要等待。
  • Semaphore是JUC包中的一个很简单的工具类,用来实现多线程下对于资源的同一时刻的访问线程数限制
  • Semaphore中存在一个【许可】的概念,即访问资源之前,先要获得许可,如果当前许可数量为0,那么线程阻塞,直到获得许可。
  • Semaphore内部使用AQS实现,由抽象内部类Sync继承了AQS。因为Semaphore天生就是共享的场景,所以其内部实际上类似于共享锁的实现。
  • 共享锁的调用框架和独占锁很相似,它们最大的不同在于获取锁的逻辑——共享锁可以被多个线程同时持有,而独占锁同一时刻只能被一个线程持有。
  • 由于共享锁同一时刻可以被多个线程持有,因此当头节点获取到共享锁时,可以立即唤醒后继节点来争锁,而不必等到释放锁的时候。因此,共享锁触发唤醒后继节点的行为可能有两处,一处在当前节点成功获得共享锁后,一处在当前节点释放共享锁后。
  • 采用semaphore来进行限流的话会产生突刺现象。【突刺现象:指在一定时间内的一小段时间内就用完了所有资源,后大部分时间中无资源可用。比如在限流方法中的计算器算法,设置1s内的最大请求数为100,在前100ms已经有了100个请求,则后面900ms将无法处理请求。

参考:https://cloud.tencent.com/developer/article/1801558

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

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

相关文章

YOLOv10涨点改进SPPF创新结构,重新设计全局平均池化层和全局最大池化层,增强全局视角信息和不同尺度大小的特征

本文改进:SPPF_improve利用全局平均池化层和全局最大池化层,加入一些全局背景信息和边缘信息,从而获取全局视角信息并减轻不同尺度大小所带来的影响,强烈推荐,适合直接使用,paper创新级。 目录 1,YOLOv10介绍 1.1 C2fUIB介绍 1.2 PSA介绍 1.3 SCDown 2.SPP &SP…

React 中的事件处理

React 中是如何处理事件的&#xff0c;现在下面简单的一段代码&#xff1a; export default function App() {const AList lazy(()>import(./List.js))const r useRef(null) const [show, setShow] useState(false);return (<><button onFocus{()>{setShow…

如何用 ChatGPT DALL-E3绘画(10个案例)

如何用ChatGPT绘画——10个案例&#xff08;附提示词&#xff09; DALL•E 3可以在ChatGPT plus里直接使用了。 如果想免费使用&#xff0c;可以用新必应免费使用。 上次有个朋友问&#xff1a;DALL•E 3 有什么用。 这里用十个案例&#xff0c;来解释一下这个问题。 1.创…

国产MCU芯片(1):航顺芯片MCU概览

前言: 截止2023年底,全国有3451家芯片设计公司,已经IPO的就有168家,尚未IPO的3283家中超过一半的年营收在1000万以下,迅猛发展的几年的确有些国产芯片开始站上赛道,这也是国际大背景下的一种必然选择,毕竟突然间出现的大市场需要国产顶上,但资本市场是周期性的,国产替…

扫地机LiDAR形态之美

石头扫地机V20 LiDAR: Flash光源和Spot光源切换 图来自 Robot森 LiDAR(Light Detection and Ranging,激光雷达)技术在扫地机器人中的应用,不仅提升了机器的智能性和实用性,还展现了一种科技与艺术的融合之美。 一、外观设计的精致性 紧凑与轻巧:扫地机器人的LiDAR传感器…

qt笔记之main.cpp加载qml文件的3种方法QQuickView 、QQmlApplicationEngine、QQuickWidget

qt笔记之main.cpp加载qml文件的3种方法QQuickView 、QQmlApplicationEngine、QQuickWidget —— 2024-06-16 下午 code review! 文章目录 qt笔记之main.cpp加载qml文件的3种方法QQuickView 、QQmlApplicationEngine、QQuickWidget1.使用qtcreator创建qml空项目&#xff0c;默…

手把手教你如何在Windows11下安装Docker容器

文章的主要要点&#xff1a; 为什么使用Docker&#xff1a;Docker可以简化部署过程&#xff0c;特别适合新手或在学习新技能&#xff08;如Redis、MySQL、消息队列、Nginx等&#xff09;时使用。 安装前的准备&#xff1a;在安装Docker之前&#xff0c;需要在Windows中开启一些…

ModuleNotFoundError: No module named ‘distutils‘的解决办法

最近想试试odoo17&#xff0c;在windows环境下&#xff0c;想安装试验一下&#xff0c;结果老出现oduleNotFoundError: No module named ‘distutils‘错误。查了一下&#xff0c;以为是python版本导致的&#xff0c;结果试了很多版本如下&#xff1a; 试了几个&#xff0c;每个…

HLS入门实验

文章目录 一、HLS介绍1.1 什么是HLS1.2HLS与VHDL/Verilog编程技术有什么关系?1.3HLS的关键技术和技术局限性1.3.1关键技术1.3.2 技术局限性 二、HLS入门实验2.1安装Vivado2.2创建项目2.3添加文件2.4仿真2.5创建Vivado工程2.6生成IP核2.7添加代码 参考 一、HLS介绍 1.1 什么是…

【C语言】解决C语言报错:Race Condition

文章目录 简介什么是Race ConditionRace Condition的常见原因如何检测和调试Race Condition解决Race Condition的最佳实践详细实例解析示例1&#xff1a;缺乏适当的同步机制示例2&#xff1a;错误使用条件变量 进一步阅读和参考资料总结 简介 Race Condition&#xff08;竞争条…

计算缺失msvcr120.dll文件怎么办,msvcr120.dll丢失的解决方法分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到msvcr120.dll”。那么&#xff0c;msvcr120.dll到底是什么&#xff1f;为什么计算机会找不到它&#xff1f;它会对计算机产生什么具体影响&#xff1f;如何解决这个问题&#xff1f;…

证照之星 XE版软件怎么下载安装? 【详细安装图文教程】

软件简介&#xff1a; 证照之星是国内顶级的证件照片制作软件&#xff0c;具有一键裁剪&#xff0c; 智能背景替换&#xff0c;批量制作、内置证照规格的四大优势。同时两大独创技术&#xff1a;智能去除皮肤油光、证照服装替换。同时支持联机拍摄&#xff1a;支持网络摄像头及…

linux中批量给文件改名

rename 需要批量将文件名前的UC-10_取消掉&#xff0c;以数字来命名文件 rename s/UC-10_// *.jpg 修改成功 要是修改为其他名字需要在单引号的第二个/后加字符即可 例如要改为li

一段代码读取Chrome存储的所有账号密码和Cookie

先写结论&#xff1a; Chrome密码管理里的账号密码&#xff0c;还有Cookie&#xff0c;安全性并不算太高&#xff0c;一段代码就可以自动读取并上报到其它地方。 尤其是国内用户大多喜欢破解软件&#xff0c;这些软件只要注入这样一段代码&#xff0c;就无声无息的把你的所有账…

74HC04做陶瓷和晶体振荡器实验初步

面包板&#xff0c;电压5V 17.6Mhz晶振&#xff0c;起振OK 其他的465K&#xff0c;1M&#xff0c;4M&#xff0c;10M&#xff0c;16M&#xff0c;20M陶瓷不起振 更换过Rf也不起作用&#xff0c;待研究。 rf参考&#xff0c;这是人家博客给出的。 还看到一个文章说&#xff…

框架的使用

什么是框架&#xff1f; 盖房子&#xff0c;框架结构 框架结构就是房子主体&#xff0c;基本功能 把很多基础功能已经实现&#xff08;封装了&#xff09; 框架&#xff1a;在基础语言之上&#xff0c;对各种基础功能进行封装&#xff0c;方便开发者&#xff0c;提高开发效…

3D ToF赋能小米CyberDog 2提升视觉灵敏度

随着科技的进步,智能机器人越来越多地融入我们的日常生活。其中,CyberDog 2作为一款前沿的四足机器人,凭借其出色的视觉灵敏度和多功能技术配备,受到了广泛的关注。本文将重点探讨CyberDog 2的视觉系统,尤其是其四种不同类型的摄像头如何共同提升其视觉灵敏度,以及激光传…

书生·浦语大模型实战营第二期作业六

1、安装环境&#xff1a; 2、安装legent和agentlego&#xff1a; 3、部署apiserver&#xff1a; 4、legent web demo&#xff1a; 5、没搜到&#xff0c;很尴尬&#xff1a; 6、自定义工具&#xff1a; 7、智能体“乐高”&#xff1a; 8、智能体工具&#xff0c;识别图片&#…

C++ 矩阵乘法

描述 如果A是个x行y列的矩阵&#xff0c;B是个y行z列的矩阵&#xff0c;把A和B相乘&#xff0c;其结果将是另一个x行z列的矩阵C。这个矩阵的每个元素是由下面的公式决定的 矩阵的大小不超过100*100 输入描述&#xff1a; 第一行包含一个正整数x&#xff0c;代表第一个矩阵的…

Vue32-挂载流程

一、init阶段 生命周期本质是函数。 1-1、beforeCreate函数 注意&#xff1a; 此时vue没有_data&#xff0c;即&#xff1a;data中的数据没有收到。 1-2、create函数 二、生成虚拟DOM阶段 注意&#xff1a; 因为没有template选项&#xff0c;所以&#xff0c;整个div root都…