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:别浪,等人齐再团!



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

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

相关文章

机房合作—我是组长

五一期间开始机房合作,到现在一个多星期了。我,蕾蕾,亮亮一组,我担任组长一职。在着手准备项目开始之前,我们听取了各位师父的一些建议,也算是给我们指明一下方向。第一天晚上,我召开了我们项目…

golang的new函数_new()和make()函数以及Golang中的示例

golang的new函数In Golang, to allocate memory, we have two built-in functions new() and make(). 在Golang中,要分配内存,我们有两个内置函数new()和make() 。 1)new()函数 (1) new() function) Memory returned by new() is zeroed. new()返回的内…

Android Activity和Intent机制学习笔记

文章转自:http://www.cnblogs.com/feisky/archive/2010/01/16/1649081.html Activity Android中,Activity是所有程序的根本,所有程序的流程都运行在Activity之中,Activity具有自己的生命周期(见http://www.cnblogs.com…

scala中字符串计数_如何在Scala中创建一系列字符?

scala中字符串计数The range is a set of data from a lower value to a larger value. In Scala, we have an easy method to create a range using to keyword. 范围是从较低值到较大值的一组数据。 在Scala中,我们有一种使用to关键字创建范围的简单方法。 Synta…

多域名解析到同一网站C的php重定向代码

在index.php最前面加上以下代码&#xff1a; <?php if(strpos($_SERVER[HTTP_HOST],afish.cnblogs.com)false){#header(Location: http://afish.cnblogs.com/);echo <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org…

android 中 gravity 和 layout_gravity 的区别

文章转自&#xff1a;http://blog.csdn.net/feng88724/article/details/6333809 在进行UI布局的时候&#xff0c;可能经常会用到 android:gravity 和 android:layout_Gravity 这两个属性。 关于这两个属性的区别&#xff0c;网上已经有很多人进行了说明&#xff0c;这边再简…

线程的故事:我的3位母亲成就了优秀的我!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;声明&#xff1a;本故事纯属虚构&#xff0c;如果雷同那就是真事了&#xff01;大家好&#xff0c;我是线程&#xff0c;我的…

c ++查找字符串_C ++结构| 查找输出程序| 套装3

c 查找字符串Program 1: 程序1&#xff1a; #include <iostream>#pragma pack(1)using namespace std;typedef struct{int A;int B;char c1;char c2;} S;int main(){cout << sizeof(S);return 0;}Output: 输出&#xff1a; In a 32-bit system: 10In a 64-bit sy…

7种内存泄露场景和13种解决方案!

前言Java通过垃圾回收机制&#xff0c;可以自动的管理内存&#xff0c;这对开发人员来说是多么美好的事啊。但垃圾回收器并不是万能的&#xff0c;它能够处理大部分场景下的内存清理、内存泄露以及内存优化。但它也并不是万能的。不然&#xff0c;我们在实践的过程中也不会出现…

Android如何关闭Application

转载自&#xff1a;http://blog.csdn.net/hello_kevinkuang/article/details/7443005 程序启动后&#xff0c;先执行Application.onCreate()&#xff0c;再执行Activity.onCreate()。如果没有生成自己的Application&#xff0c;那么系统会为你自动生成一个。退出程序时我们一般…

Android Sqlite

2019独角兽企业重金招聘Python工程师标准>>> 一、http://lansuiyun.iteye.com/blog/1246430 good! 二、增加新表时&#xff0c;需要更新版本号。 三、Android 使用自定义cursorAdapter&#xff1a; http://blog.csdn.net/buaalei/article/details/6064792 good! 四…

优雅统计代码耗时的4种方法!

来源&#xff1a;jitwxs.cn/5aa91d10.html一、前言代码耗时统计在日常开发中算是一个十分常见的需求&#xff0c;特别是在需要找出代码性能瓶颈时。可能也是受限于 Java 的语言特性&#xff0c;总觉得代码写起来不够优雅&#xff0c;大量的耗时统计代码&#xff0c;干扰了业务逻…

小黑小波比.git clone报错解决方案

2019独角兽企业重金招聘Python工程师标准>>> zmzpzmzp1:~/data$ git clone git192.168.199.199:zmw/s910.git 正克隆到 s910... ssh: connect to host 192.168.199.199 port 22: Connection refused fatal: Could not read from remote repository.Please make sure…

Android 运行时异常 Binary XML file line # : Error inflating class

今天在做一个二维码扫描的项目的时候出现了一个错误&#xff1a; android.view.InflateException: Binary XML file line #12: Error inflating class com.zxing.view.ViewfinderView 项目里面的代码是我从以前项目里面拷贝的&#xff0c;可是各种报错&#xff0c;找了很多资…

一个sql注入直接把我们服务搞挂了

前言最近我在整理安全漏洞相关问题&#xff0c;准备在公司做一次分享。恰好&#xff0c;这段时间团队发现了一个sql注入漏洞&#xff1a;在一个公共的分页功能中&#xff0c;排序字段作为入参&#xff0c;前端页面可以自定义。在分页sql的mybatis mapper.xml中&#xff0c;orde…

reinterpret_cast和static_cast的总结

主要参考&#xff1a;http://blog.csdn.net/querw/article/details/7387594 http://www.cnblogs.com/jerry19880126/archive/2012/08/14/2638192.html http://www.cnblogs.com/ider/archive/2011/07/30/cpp_cast_operator_part3.htmlhttp://bbs.csdn.net/topics/390249118 关键…

Android实现点击两次返回键退出

转自 http://blog.sina.com.cn/s/blog_4fd2a65a0101gg2o.html 在做安卓应用是我们经常要判断用户对返回键的操作&#xff0c;一般为了防止误操作都是在用户连续按下两次返回键的时候提示用户是否退出应用程序。 第一种实现的基本原理就是&#xff0c;当按下BACK键时&#xff0c…

c#hello world_C#| 打印消息/文本(用于打印Hello world的程序)

c#hello worldTo print the message/text or any value – we use two functions: 要打印消息/文本或任何值–我们使用两个功能&#xff1a; Console.Write (); Console.Write(); This function displays text, values on the output device and does not insert a new line …

Java双刃剑之Unsafe类详解

前一段时间在研究juc源码的时候&#xff0c;发现在很多工具类中都调用了一个Unsafe类中的方法&#xff0c;出于好奇就想要研究一下这个类到底有什么作用&#xff0c;于是先查阅了一些资料&#xff0c;一查不要紧&#xff0c;很多资料中对Unsafe的态度都是这样的画风&#xff1a…

Java知多少(66)输入输出(IO)和流的概述

输入输出&#xff08;I/O&#xff09;是指程序与外部设备或其他计算机进行交互的操作。几乎所有的程序都具有输入与输出操作&#xff0c;如从键盘上读取数据&#xff0c;从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息&#xff0c;或者是把信…