Java并发基础:Semaphore全面解析!

news2025/4/16 2:35:19

Java并发基础:Semaphore全面解析! - 程序员古德

内容概要

Semaphore通过控制许可数量,实现了对并发线程数的精细管理,有效避免了资源竞争和过载问题,能显著提升系统吞吐量和响应速度,同时,Semaphore还支持公平与非公平策略,具有更好的灵活性和适应性,满足了不同业务场景的需求。

核心概念

Semaphorejava.util.concurrent中非常有用的并发编程工具类,它通常被用于限制对某个资源或资源池的并发访问数量。举个实际的例子:假设一个餐厅里只有 10 张桌子,在繁忙的用餐时段,很多顾客会同时来到餐厅,但餐厅的空间有限,不能同时容纳所有顾客,这时,就需要一种机制来控制进入餐厅的顾客数量,确保餐厅不会过于拥挤。

Semaphore 就可以扮演这个控制者的角色,可以将 Semaphore 的许可数设置为 10,这代表着餐厅里最多可以有 10 组顾客同时用餐,每当有顾客进入餐厅并坐下时,Semaphore 的许可数就会减 1;每当有顾客用餐完毕离开时,Semaphore 的许可数就会加 1,如果所有桌子都坐满了,后来的顾客就需要在门外等待,直到有桌子空出来。

在这种业务场景下,使用Semaphore 就可以有效地控制餐厅的拥挤程度,保证了顾客的用餐体验。

具体来说,Semaphore 通常用于以下场景:

  1. 限制并发访问量:当一个系统或应用需要限制对某个共享资源(如数据库连接、文件、网络服务等)的并发访问量时,Semaphore 可以用来控制同时访问这些资源的线程数,通过设置 Semaphore 的许可数,可以确保不会有过多的线程同时访问资源,从而防止资源过载或争用条件导致的性能下降。
  2. 实现线程同步:除了限制并发访问量外,Semaphore 还可以用于协调多个线程的执行顺序,例如,在一个多线程程序中,如果某个操作需要多个线程按照特定的顺序执行,可以使用 Semaphore 来控制这些线程的执行流程。
  3. 资源池管理Semaphore 还可以用于管理资源池,例如连接池、线程池等,通过动态调整 Semaphore 的许可数,可以根据系统的负载情况动态地增加或减少资源的使用量,从而提高系统的伸缩性和资源利用率。

Semaphore 是一种灵活的同步工具,它非常适合在信号量场景中控制对资源的访问,能够在多线程环境中提供细粒度的控制,帮助开发者有效地管理系统资源,比如,用来控制对数据库连接、线程池资源或其他共享资源的并发访问,从而避免资源争用和系统过载,保证系统的稳定性和性能。

代码案例

下面是一个简单的Java代码示例,演示了如何使用Semaphore来限制对一组资源的并发访问,如下代码:

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Semaphore;  
  
public class SemaphoreExample {  
  
    // 创建一个Semaphore,初始许可为3,表示资源池中最多有3个资源可用  
    private static final Semaphore semaphore = new Semaphore(3);  
  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池来模拟客户端请求  
        ExecutorService executor = Executors.newFixedThreadPool(5);  
  
        // 提交10个任务到线程池,每个任务代表一个客户端请求  
        for (int i = 0; i < 10; i++) {  
            executor.submit(() -> {  
                try {  
                    // 线程尝试获取许可  
                    semaphore.acquire();  
                    System.out.println("线程" + Thread.currentThread().getName() + "获取到资源,开始处理...");  
  
                    // 模拟资源处理时间  
                    Thread.sleep((long) (Math.random() * 1000));  
  
                    System.out.println("线程" + Thread.currentThread().getName() + "处理完毕,释放资源...");  
  
                    // 线程释放许可  
                    semaphore.release();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            });  
        }  
  
        // 关闭线程池  
        executor.shutdown();  
    }  
}

在上面代码中,创建了一个Semaphore实例,初始许可设置为3,这意味着最多只能有3个线程同时访问资源,然后创建了一个固定大小为5的线程池来模拟客户端请求,提交了10个任务到线程池,每个任务都尝试获取Semaphore的许可,模拟资源的访问。

当线程调用semaphore.acquire()时,它会尝试获取一个许可,如果许可可用,线程将继续执行;如果没有许可可用,线程将被阻塞,直到有许可可用。当线程完成资源访问后,它调用semaphore.release()来释放许可,这样其他等待的线程就可以获取许可并访问资源了。

如下输出结果:

线程pool-1-thread-1获取到资源,开始处理...  
线程pool-1-thread-2获取到资源,开始处理...  
线程pool-1-thread-3获取到资源,开始处理...  
线程pool-1-thread-3处理完毕,释放资源...  
线程pool-1-thread-1处理完毕,释放资源...  
线程pool-1-thread-4获取到资源,开始处理...  
线程pool-1-thread-2处理完毕,释放资源...  
线程pool-1-thread-5获取到资源,开始处理...  
线程pool-1-thread-4处理完毕,释放资源...  
线程pool-1-thread-5处理完毕,释放资源...  
线程pool-1-thread-3获取到资源,开始处理...  
线程pool-1-thread-3处理完毕,释放资源...

从输出中可以看到,尽管有一个大小为5的线程池,但Semaphore确保了同时访问资源的线程数不超过3个,当线程处理完资源后,它会释放许可,允许其他线程获取许可并继续执行。

核心API

下面是 Semaphore 类中一些重要方法的解释:

acquire()

此方法用于获取一个许可,如果当前没有可用的许可,则当前线程会被阻塞,直到有许可可用,这是 Semaphore 最基本的使用方式,用于控制对资源的访问。

acquire(int permits)

这个方法允许一次性获取多个许可,如果当前可用许可数量少于请求的数量,则线程会被阻塞,直到有足够的许可可用。

acquireUninterruptibly()

acquireUninterruptibly()acquireUninterruptibly(int permits)这两个方法与 acquire 类似,但区别在于它们不会响应中断,即使其他线程中断了正在等待的线程,这些线程也会继续等待,直到获得许可。

tryAcquire

tryAcquire()tryAcquire(int permits)这两个方法尝试获取一个或多个许可,如果当前有可用的许可则立即返回 true,并且获取成功;如果没有可用许可则立即返回 false,线程不会被阻塞。

tryAcquire(long timeout, TimeUnit unit)

tryAcquire(long timeout, TimeUnit unit)和tryAcquire(int permits, long timeout, TimeUnit unit)这两个方法尝试在给定的时间内获取一个或多个许可,如果在指定的时间内获得了许可,则返回 true;否则,返回 false,如果线程在等待期间被中断,那么这两个方法都会抛出 InterruptedException

release

release()此方法用于释放一个许可,将其返回给 Semaphore,这样其他等待的线程就可以获取它,通常在完成对资源的访问后调用此方法。

release(int permits)

release(int permits)这个方法允许一次性释放多个许可。

availablePermits

availablePermits()这个方法返回当前 Semaphore 中可用的许可数量。

hasQueuedThreads()

检查是否有任何线程正在等待获取许可。

getQueueLength()

返回正在等待获取许可的线程数。

drainPermits()

此方法返回并删除当前所有可用的许可,主要用于一些特殊的场景,比如需要在某个时刻一次性消耗所有许可。

reducePermits(int reduction)

这个方法用于减少 Semaphore 中的可用许可数量,通常用于一些动态调整资源池大小的场景,需要注意的是,这个方法不会阻塞,如果减少后的许可数小于0,那么会抛出 IllegalArgumentException

注意:Semaphore 并不保证获取许可的公平性,即等待时间最长的线程不一定会优先获得许可。

核心总结

Java并发基础:Semaphore全面解析! - 程序员古德

优点

  1. 简单易用:通过许可数量,轻松控制并发线程数。
  2. 高效:避免了不必要的线程创建和销毁,提高了系统吞吐量。
  3. 灵活:支持公平和非公平策略,可根据需求选择。

缺点

  1. 不保证公平性:默认策略下,先到的线程不一定先获得许可。
  2. 可能导致死锁:使用不当(如在持有Semaphore时执行其他阻塞操作)时,可能会引发死锁。

关注我,每天学习互联网编程技术 - 程序员古德

END!

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

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

相关文章

nodejs+vue+ElementUi家庭美食菜谱分享网站_in9c2

&#xff08;设计制作有一定的安全性&#xff1b;数据库方面主要采用的是MySQL来进行开发&#xff0c;其特点是稳定性好&#xff0c;数据库存储容量大&#xff0c;处理能力快等优势&#xff1b;服务器采用的是Tomcat服务&#xff0c;能够提供稳固的运行平台&#xff0c;确保系统…

【Redis】实现缓存及相关问题

Redis实现缓存及相关问题 认识缓存 缓存就是数据交换的缓冲区&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高。 缓存的作用&#xff1a; 降低后端负载提高读写效率&#xff0c;降低响应时间 缓存的成本&#xff1a; 数据一致性成本代码维护成本运维成本 …

jQuery为动态添加的子元素添加点击事件

如图&#xff0c;查看学员信息后&#xff0c;相对其信息做相应处理&#xff0c;给后面的管理添加点击功能 通过点击查看全部学员 $.each(pagedata, function (index, item) { // 性别转换 if(item.sex1){sex_1 "男"…

Banana Pi BPI-R4开源路由器开发板快速上手用户手册,采用联发科MT7988芯片设计

介绍 Banana Pi BPI-R4 路由器板采用 MediaTek MT7988A (Filogic 880) 四核 ARM Corex-A73 设计&#xff0c;4GB DDR4 RAM&#xff0c;8GB eMMC&#xff0c;板载 128MB SPI-NAND 闪存&#xff0c;还有 2x 10Gbe SFP、4x Gbe 网络端口&#xff0c;带 USB3 .2端口&#xff0c;M.2…

虚拟机安装archlinux

1、创建虚拟机 2、安装系统4、为了方便&#xff0c;修改密码并使用dos窗口连接 5、磁盘分区 由于新建虚拟机时是8G&#xff0c;所以只建一个分区就行 6、格式化分区并挂载 7、更新镜像 rootarchiso ~ # pacman -Sy 8、 pacstrap -i /mnt base base-devel linux linux-f…

RabbitMQ概念

一 、RabbitMQ概念 1 架构图 2 相关概念 Publisher - ⽣产者&#xff1a;发布消息到RabbitMQ中的Exchange Consumer - 消费者&#xff1a;监听RabbitMQ中的Queue中的消息 Broker&#xff1a;接收和分发消息的应用&#xff0c;RabbitMQ Server就是 Message Broker&#xf…

精酿啤酒:发酵过程中的温度控制与效果

在啤酒酿造过程中&#xff0c;发酵温度的控制重要&#xff0c;它不仅影响酵母菌的活性&#xff0c;还决定了啤酒的口感、香气和风味。对于Fendi Club啤酒来说&#xff0c;切确控制发酵温度是确保啤酒品质和口感的关键环节。 在Fendi Club啤酒的发酵过程中&#xff0c;温度控制尤…

dnslog在sql盲注

首先必须保证sql是在windows下 因为需要使用到UNC路径 保证mysql中的secure_file_priv为空 secure_file_priv为null&#xff0c;load_file则不能加载文件。 secure_file_priv为路径&#xff0c;可以读取路径中的文件&#xff1b; secure_file_priv为空&#xff0c;可以读取磁盘…

Android 12.0 应用中监听系统收到的通知

Android 12.0 通知简介https://blog.csdn.net/Smile_729day/article/details/135502031?spm1001.2014.3001.5502 1. 需求 在系统内置应用中或者在第三方应用中,获取Android系统收到的通知的内容. 2. NotificationListenerService 接口 Android 系统预留了专门的API, 即 No…

Unity - 调节camera物理相机参数(HDRP)

在 “Hierarchy” 右键 -> Volume -> Global Volume new 一个 profile, 设置Mode为Pysical Camera 再点击camera组件&#xff0c;这时候设置 ISO、Shutter Speed、Aperture等参数值还会有效。

[R] Why data manipulation is crucial and sensitive?

What does a data scientist really do? Identifying the pattern in cultural consumption, making fancy graph, engage a dialogue between data and the existing literature, refining hypothesis….(done within one months with three to four online meetings with p…

漏洞01-目录遍历漏洞/敏感信息泄露/URL重定向

目录遍历漏洞/敏感信息泄露/URL重定向 文章目录 目录遍历敏感信息泄露URL重定向 目录遍历 敏感信息泄露 于后台人员的疏忽或者不当的设计&#xff0c;导致不应该被前端用户看到的数据被轻易的访问到。 比如&#xff1a; ---通过访问url下的目录&#xff0c;可以直接列出目录下…

【实战】使用Helm在K8S集群安装MySQL主从

文章目录 前言技术积累什么是HelmStorageClass使用的工具版本 helm 安装 MySQL 1主2从1. 添加 bitnami 的仓库2. 查询 MySQL 资源3. 拉取 MySQL chart 到本地4. 对chart 本地 values-test.yaml 修改5. 对本地 templates 模板 修改6. 安装 MySQL 集群7. 查看部署的 MySQL 集群8.…

算法--数论

这里写目录标题 质数&#xff08;素数&#xff09;定义判断是否为质数暴力写法&#xff0c;试除法基本思想具体写法 优化基本思想&#xff08;时间复杂度根号n&#xff09;具体写法 分解质因数分析题意暴力写法基本思想具体代码 优化基本思想&#xff08;时间复杂度小于等于根号…

聊聊ClickHouse MergeTree引擎的固定/自适应索引粒度

前言 我们在刚开始学习ClickHouse的MergeTree引擎时&#xff0c;就会发现建表语句的末尾总会有SETTINGS index_granularity 8192这句话&#xff08;其实不写也可以&#xff09;&#xff0c;表示索引粒度为8192。在每个data part中&#xff0c;索引粒度参数的含义有二&#xf…

Camera | 15.闪光灯SGM3141概述

芯片说明 SGM3141是一种电流调节降压/升压电荷泵LED驱动器&#xff0c;能够驱动700M输出电流。它非常适合为相机闪光灯应用的高亮度LED供电。SGM3141具有1/2操作模式&#xff0c;用于控制闪光和火炬模式的输出电流。 电源电压在2.7V到5.5V之间工作&#xff0c;非常适合由1芯锂…

CDS view与替代对象

一&#xff0c;简介 替代对象是指用一个CDS view指派给一个透明表或常规数据库视图&#xff0c;使得透明表或常规数据库视图的访问重定向到该CDS view。 替代有诸多要求&#xff1a; 字段数量一致且同名对应&#xff0c;顺序可以不一致对应的字段数据类型长度等必须一致CDS v…

文心一言APP上线新功能,一张照片、三句话即可生成专属数字分身

只需一张照片、录制三句话&#xff0c;就能拥有一个自己的数字分身&#xff1f;这不是科幻电影&#xff0c;而是文心一言APP上线的新功能 - 数字分身。 目前&#xff0c;文心一言APP正在内测数字分身新功能&#xff0c;明天起&#xff0c;iOS和Android用户升级新版本后&#xf…

超简单设置Windows共享文件夹,传输文件无烦恼

前言 开始之前&#xff0c;先让小白感叹一下科技发展真快呀&#xff01;&#xff08;这句话纯粹是为了凑点字数&#xff09; 随着科技的发展&#xff0c;人们手上总会有各种各样的电子设备&#xff1a;电脑、平板、手机、游戏机、电视盒子等等&#xff5e; 有时候想要传输个文…

【Docker】【深度学习算法】在Docker中使用gunicorn启动多个并行算法服务,优化算法服务:从单进程到并行化

文章目录 优化算法服务&#xff1a;从单进程到并行化单个服务架构多并行服务架构Docker化并指定并行服务数量 扩展知识 优化算法服务&#xff1a;从单进程到并行化 在实际应用中&#xff0c;单个算法服务的并发能力可能无法满足需求。为了提高性能和并发处理能力&#xff0c;我…