Semaphore自白:限流器用我就对了!

2024-02-10 21:18
文章标签 限流 semaphore 自白 器用

本文主要是介绍Semaphore自白:限流器用我就对了!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

大家好,我是 Semaphore,我的中文名字叫“信号量”,我来自 JUC 家族(java.util.concurrent)。

我们家族有很多优秀的成员,比如:CountDownLatch:等待其他线程都执行完再执行某线程,CyclicBarrier:循环阻塞一组线程,直到某个事件达成,当然我也不比他们弱哦 罒ω罒。

以下是我的个人简历,希望各位读者老爷们给个好评和三连,先在此谢过了~

基本信息

  • 姓名:Semaphore

  • 中文名:(计数)信号量

  • 出生日期:JDK 1.5

  • 籍贯:JUC(java.util.concurrent)

  • 用途:Java 中的一个同步器,与 CountDownLatch 和 CyclicBarrier 不同,Semaphore 是用来管理许可证的,线程在调用 acquire() 方法时,如果没有许可证,那么线程就会阻塞等待,直到有许可证时才能继续执行。许可证使用 release() 方法来发布(发布一个许可证),调用 acquire() 方法时,如果有证书会减少许可证并继续执行后面的代码,如果没有证书只能阻塞等待许可证,而 Semaphore 在创建时会声明许可证的最大数量。

专业技能

我的专业技能就是“管理证书”,使用此技能可以轻松的实现「限流」功能

什么是限流?

比如五一小长假快到了,到那时会有大量的人去各个景区游玩,但是每个景区能容纳的人是有限的,比如大西安的大唐芙蓉园,它的日承载量是 6 万人次,也就是说每天最多能让 6 万来这里游玩,但五一的时候会来很多的人,比如突然来了 10 万人,那这个时候就只能「限流」排队等待入园了。

也就说,大唐芙蓉园会让 6 万人先进去玩,剩余的人在门口等待排队,当有人从里面出来的时候,才允许另一个排队的人进去。工作人员会把人数始终控制在 6 万人以下,这样做的目的是为了让游玩的人有一个好的体验,不至于造成一些意外事故,比如踩踏事件什么的,一定程度上保证了社会的稳定,也便于景区良好的口碑建立和日后的正常运营,而这种排队限制最大人数的行为就是「限流」

再来举个例子,比如以车辆的限号来说,它也是限流的一种常见场景。这样做的好处,一方面是可以保护环境尽可能少一些碳排放,另一方面能有效的缓解上、下班高峰时段的拥堵情况。尤其是在大西安,很难想象如果不限号,那么会堵成什么样?(PS:让原本本不富裕的生活更是雪上加霜...)

咱们再从生活中的事例回到程序当中,假设一个程序只能为 10W 人提供服务,突然有一天因为某个热点事件,造成了系统短时间内的访问量迅速增加到了 50W,那么导致的直接结果是系统崩溃,任何人都不能用系统了,显然只有少人数能用远比所有人都不能用更符合我们的预期,因此这个时候我们要使用「限流」了。

使用Semaphore实现限流

Semaphore 在创建的时候可以设置证书的数量,相当于设置了限流的最大值,再通过 release() 方法来发放证书,通过 acquire() 方法来阻塞并等待证书,这样就通过控制证书的方式来实现限流功能了。

项目经验

接下来,咱们使用代码的方式来演示 Semaphore 的使用。我们以停车场的限流为例,假设整个停车场只有 2 个车位(车位虽少,但足矣说明问题),但来停车的却有 5 辆车,显然车位不够用了,此时需要保证停车场最多只能有 2 辆车,接下来咱们使用 Semaphore 来实现车辆的限流功能,具体实现代码如下:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;/*** Author:磊哥* By:Java中文社群*/
public class SemaphoreExample {// 创建信号量static Semaphore semaphore = new Semaphore(2);public static void main(String[] args) {// 创建 5 个固定的线程数ExecutorService threadPool = Executors.newFixedThreadPool(5);// 定义执行任务Runnable runnable = new Runnable() {@Overridepublic void run() {// 拿到当前线程的名称String tname = Thread.currentThread().getName();System.out.println(String.format("老司机:%s,停车场外排队,时间:%s",tname, new Date()));try {// 执行此行,让所有线程先排队等待进入停车场Thread.sleep(100);// 执行阻塞semaphore.acquire();System.out.println(String.format("老司机:%s,已进入停车场,时间:%s",tname, new Date()));Thread.sleep(1000);System.out.println(String.format("老司机:%s,离开停车场,时间:%s",tname, new Date()));// 释放锁semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}};// 执行任务 1threadPool.submit(runnable);// 执行任务 2threadPool.submit(runnable);// 执行任务 3threadPool.submit(runnable);// 执行任务 4threadPool.submit(runnable);// 执行任务 5threadPool.submit(runnable);// 等线程池任务执行完之后关闭threadPool.shutdown();}
}

以上代码的执行结果如下:

从上述的结果我们可以看出,当有 5 辆车同时需要进入停车场时,因为停车场的停车位只有 2 个,所以停车场最多只能容纳 2 辆车。此时我们通过 Semaphore 的 acquire 方法(阻塞等待)和 release 方法(颁发一个证书)顺利的实现了限流的功能,让停车场的车辆数始终控制在 2 辆车以下(等于或小于 2 辆车)。

个人评价

我(Semaphore)实现证书控制手段有两种,一种公平模式和非公平模式,当然为了执行的性能考虑,默认情况下我采取的是非公平的方式,具体实现可见源码:

public Semaphore(int permits) {sync = new NonfairSync(permits); // 非公平模式
}
关于公平模式和非公平模式

所谓的公平模式就是以调用 acquire() 的先后顺序来决定获取许可证的顺序的,公平模式遵循先进先出(FIFO)原则;而非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证,而前面还有等待的线程。

显然使用非公平的模式性能更高,因为它会把许可证发放给刚好准备好的线程,而不用再根据先后顺序去“叫号”了。

使用公平模式

当然,你可以手动选择使用公平模式来运行 Semaphore,Semaphore 提供了两个构造函数,源码如下:

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

如果想用公平模式就可以使用第二个构造函数 Semaphore(int permits, boolean fair),将 fair 值设置为 true 就是公平模式来获取证书了。

其他补充

我还提供了一些其他方法,用于实现更多的功能,详情如下:

  • int availablePermits():返回此信号量中当前可用的许可证数。

  • int getQueueLength():返回正在等待获取许可证的线程数。

  • boolean hasQueuedThreads():是否有线程正在等待获取许可证。

  • boolean isFair():查询 Semaphore 使用的是公平模式还是非公平模式,如果此信号量使用的是公平模式则返回 true。

  • void release(int permits):释放给定数量的许可证,将其返回到信号量。

  • tryAcquire():从这个信号量获得许可证,只有在调用时可以使用该许可证。

  • tryAcquire(int permits):从这个信号量获取给定数量的许可证,只有在调用时全部可用。

  • tryAcquire(int permits, long timeout, TimeUnit unit):从该信号量获取给定数量的许可证,如果在给定的等待时间内全部可用,并且当前线程尚未 interrupted。

  • tryAcquire(long timeout, TimeUnit unit):如果在给定的等待时间内可用,并且当前线程尚未 到达 interrupted,则从该信号量获取许可。

  • void reducePermits(int reduction) :减少可用的许可证数量 reduction 个,它是 protected 方法。

  • Collection getQueuedThreads() :返回所有等待获取许可证的线程集合,它是 protected 方法。

总结

Semaphore 信号量是用来管理一组证书的,默认情况下它采取的是非公平的方式来管理证书,这样做的目的是为了实现高性能。Semaphore 中包含了两个重要的方法:release() 方法发布一个许可证书;acquire() 方法阻塞并等待一个证书。当线程调用了 acquire() 方法只有拥有了证书才能继续执行,因此可以使用 Semaphore 来实现限流。


往期推荐

CyclicBarrier:人齐了,老司机就发车了!


CountDownLatch:别浪,等人齐再团!



这篇关于Semaphore自白:限流器用我就对了!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/698076

相关文章

Java后端微服务架构下的API限流策略:Guava RateLimiter

Java后端微服务架构下的API限流策略:Guava RateLimiter 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 在微服务架构中,API限流是保护服务不受过度使用和拒绝服务攻击的重要手段。Guava RateLimiter是Google开源的Java库中的一个组件,提供了简单易用的限流功能。 API限流概述 API限流通过控制请求的速率来防止

微服务之网关安全基于Zuul并实现网关限流

微服务网关安全 微服务架构下的问题 处理安全和业务逻辑耦合,增加了复杂性和变更成本 随着业务节点增加,认证服务器压力增大 多个微服务同时暴露,增加了外部访问的复杂性 通过网关处理流程 1、请求令牌。2、转发请求。3、返回令牌。4、转发令牌各客户端应用。5、携带令牌发送请求。6、校验令牌。7、返回校验结果信息。8、访问微服务。 实例 引入依赖 <dependencies><depe

谈谈经典限流方法—漏桶、令牌桶与Guava RateLimiter的实现

大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 高并发的业务系统经常要接受大流量的考验,为了保证系统的响应度和稳定性,往往都需要对有风险的接口实施限流(rate limiting),更高大上的说法则是“流量整形”(traffic shaping)。限流的思想最初来源于计算机网络,有两种经典的方法:漏桶和令牌桶。本文先来稍微研究一下它们。

经典限流方法——漏桶、令牌桶与Guava RateLimiter的实现

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 高并发的业务系统经常要接受大流量的考验,为了保证系统的响应度和稳定性,往往都需要对有风险的接口实施限流(rate limiting),更高大上的说法则是“流量整形”(traffic shaping)。限流的思想最初来源于计算机

使用信号量实现一个限流器:C++实战指南

使用信号量实现一个限流器:C++实战指南 在现代软件开发中,限流器(Rate Limiter)是一种常用的技术,用于控制系统的请求速率,防止系统过载。信号量(Semaphore)是一种强大的同步原语,可以用于实现限流器。本文将详细介绍如何在C++中使用信号量实现一个限流器,并提供完整的代码示例和详细的解释。 什么是限流器? 限流器是一种控制系统请求速率的机制,确保在单位时间内处理的请求数量不

java 多线程 CountDownLatch、CyclicBarrier、Semaphore

在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法。   以下是本文目录大纲:   一.CountDownLatch用法   二.CyclicBarrier用法   三.Semaphore用法   若有不正之处请多多谅解,并欢迎批评指正。

Java 文件下载/上传限流算法

文章目录 一、算法思路二、限流的完整java代码实现三、注意点四、具体demo的github地址 在做文件下载功能时,为了避免下载功能将服务器的带宽打满,从而影响服务器的其他服务。我们可以设计一个限流器来限制下载的速率,从而限制下载服务所占用的带宽。 一、算法思路 定义一个数据块chunk(单位 bytes)以及允许的最大速率 maxRate(单位 KB/s)。通过maxR

常用的限流算法-令牌桶(Token Bucket)php版

令牌桶(Token Bucket)是一种常用的限流算法,用于控制流量的速率。其核心思想是以固定速率向桶中放入令牌,当请求到来时,从桶中取走一定数量的令牌,如果桶中没有足够的令牌,则拒绝请求或进行排队等待。 下面是如何在 PHP 中实现一个简单的令牌桶算法。 1. 令牌桶的基本概念 令牌的生成速度:令牌以固定速率生成并加入到桶中。桶的容量:桶中可以容纳的最大令牌数,防止令牌无限增长。请求的消耗

利用Spring Boot实现微服务的API限流策略

利用Spring Boot实现微服务的API限流策略 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! API限流是一种控制访问速率的机制,用于保护后端服务不被过载。Spring Boot提供了多种工具和方法来实现API限流策略。 API限流的概念 API限流通常通过限制在一定时间窗口内的请求数量来实现。常见的限流算法有令牌桶和漏桶算法。 使用Sprin

Java并发编程--深入理解Semaphore

Semaphore简介 Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。 Semaphore的用途 Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库