在 JUC 包下,有一个 Semaphore 类,翻译成信号量,Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。Semaphore 跟锁(synchronized、Lock)有点相似,不同的地方是,锁同一时刻只允许一个线程访问某一资源,而 Semaphore 则可以控制同一时刻多个线程访问某一资源。

Semaphore(信号量)并不是 Java 语言特有的,几乎所有的并发语言都有。所以也就存在一个信号量模型的概念,如下图所示:

信号量模型比较简单,可以概括为:一个计数器、一个队列、三个方法

计数器:记录当前还可以运行多少个资源访问资源。

队列:待访问资源的线程

三个方法

  • init():初始化计数器的值,可就是允许多少线程同时访问资源。
  • up():计数器加1,有线程归还资源时,如果计数器的值大于或者等于 0 时,从等待队列中唤醒一个线程
  • down():计数器减 1,有线程占用资源时,如果此时计数器的值小于 0 ,线程将被阻塞。

这三个方法都是原子性的,由实现方保证原子性。例如在 Java 语言中,JUC 包下的 Semaphore 实现了信号量模型,所以 Semaphore 保证了这三个方法的原子性。

Semaphore 是基于 AbstractQueuedSynchronizer 接口实现信号量模型的。AbstractQueuedSynchronizer 提供了一个基于 FIFO 队列,可以用于构建锁或者其他相关同步装置的基础框架,利用了一个 int 来表示状态,通过类似 acquire 和 release 的方式来操纵状态。关于 AbstractQueuedSynchronizer 更多的介绍,可以点击链接:

http://ifeve.com/introduce-abstractqueuedsynchronizer/

AbstractQueuedSynchronizer 在 Semaphore 类中的实现类如下:

    abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L; Sync(int permits) {
setState(permits);
} final int getPermits() {
return getState();
} final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
} 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;
}
} final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
} final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}

在 Semaphore 类中,实现了两种信号量:公平的信号量和非公平的信号量,公平的信号量就是大家排好队,先到先进,非公平的信号量就是不一定先到先进,允许插队。非公平的信号量效率会高一些,所以默认使用的是非公平信号量。具体的可以查看 Semaphore 类实现源码。

Semaphore 类中,主要有以下方法:

// 构造方法,参数表示许可证数量,用来创建信号量
public Semaphore(int permits);
// 从信号量中获取许可,相当于获取到执行权
public void acquire() throws InterruptedException;
// 尝试获取1个许可,不管是否能够获取成功,都立即返回,true表示获取成功,false表示获取失败
public boolean tryAcquire();
// 将许可还给信号量
public void release();

Semaphore 类的实现就了解的差不多了。可能你会有疑问 Semaphore 的应用场景是什么?Semaphore 可以用来限流(流量控制),在一些公共资源有限的场景下,Semaphore 可以派上用场。比如在做日志清洗时,可能有几十个线程在并发清洗,但是将清洗的数据存入到数据库时,可能只给数据库分配了 10 个连接池,这样两边的线程数就不对等了,我们必须保证同时只能有 10 个线程获取数据库链接,否则就会存在大量线程无法链接上数据库。

用 Semaphore 信号量来模拟这操作,代码如下:

public class SemaphoreDemo {
/**
* semaphore 信号量,可以限流
*
* 模拟并发数据库操作,同时有三十个请求,但是系统每秒只能处理 5 个
*/ private static final int THREAD_COUNT = 30; private static ExecutorService threadPool = Executors
.newFixedThreadPool(THREAD_COUNT);
// 初始化信号量,个数为 5
private static Semaphore s = new Semaphore(5); public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 获取许可
s.acquire();
System.out.println(Thread.currentThread().getName()+" 完成数据库操作 ,"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format( new Date()));
// 休眠两秒钟,效果更直观
Thread.sleep(2000);
// 释放许可
s.release();
} catch (InterruptedException e) {
}
}
});
}
// 关闭连接池
threadPool.shutdown();
}
}

运行效果如下:

从结果中,可以看出,每秒只有 5 个线程在执行,这符合我们的预期。

好了,关于 Semaphore 的内容就结束了,更加详细的还请您查阅相关资料和阅读 Semaphore 源码。希望这篇文章对您的学习或者工作有所帮助。

感谢您的阅读,祝好。

最后

目前互联网上很多大佬都有 Semaphore(信号量) 相关文章,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。

JUC 中提供的限流利器-Semaphore(信号量)的更多相关文章

  1. 管道限流利器pv

    pv 是什么 可不是 page view,是pipe viewer,管道偷窥器的缩写.这个东西的源站点在google code上,需要的话可以访问pv 的官网 . 这个东西的官方手册页(man pv或 ...

  2. 高并发之API接口限流

    在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再 ...

  3. java并发系列 - 第29天:高并发中常见的限流方式

    这是java高并发系列第29篇. 环境:jdk1.8. 本文内容 介绍常见的限流算法 通过控制最大并发数来进行限流 通过漏桶算法来进行限流 通过令牌桶算法来进行限流 限流工具类RateLimiter ...

  4. 高并发之 API 接口,分布式,防刷限流,如何做?

    在开发分布式高并发系统时有三把利器用来保护系统:缓存.降级.限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解 ...

  5. coding++:RateLimiter 限流算法之漏桶算法、令牌桶算法--简介

    RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类 <dependency> <groupId>com.google.guava</g ...

  6. Spring Cloud Zuul 限流详解(附源码)(转)

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法. ...

  7. Spring Cloud限流详解

    转自:https://blog.csdn.net/tracy38/article/details/78685707 在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud ...

  8. Spring Cloud限流思路及解决方案

    转自: http://blog.csdn.net/zl1zl2zl3/article/details/78683855 在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Clo ...

  9. Zuul:构建高可用网关之多维度限流

    对请求的目标URL进行限流(例如:某个URL每分钟只允许调用多少次) 对客户端的访问IP进行限流(例如:某个IP每分钟只允许请求多少次) 对某些特定用户或者用户组进行限流(例如:非VIP用户限制每分钟 ...

随机推荐

  1. Ubuntu日常使用总结

    Contents 使用了将近一年的Ubuntu,感觉不用windows也可以处理日常的事务.并且我相信只要合理利用Ubuntu,一定可以取代你手中的Windows.我不是说Ubuntu有多么好,只是从 ...

  2. windows dnsrecon

    缺了两个模块 第一个:https://pypi.python.org/pypi/netaddr 第二个:dnspython 找到下载后setup.py install就OK了,话说GFW真***了,用 ...

  3. 记 MySQL优化 20条

    1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的.当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一 ...

  4. 量化投资学习笔记31——《Python机器学习应用》课程笔记05

    用分类算法进行上证指数涨跌预测. 根据今天以前的150个交易日的数据,预测今日股市涨跌. 交叉验证的思想:将数据集D划分为k个大小相似的互斥子集,每个子集都尽可能保持数据分布的一致性,即从D中通过分层 ...

  5. 达拉草201771010105《面向对象程序设计(java)》第十八周学习总结

    达拉草201771010105<面向对象程序设计(java)>第十八周学习总结 实验十八  总复习 实验时间 2018-12-30 1.实验目的与要求 (1) 综合掌握java基本程序结构 ...

  6. Day 3 DP

    1. P1015 精卫填海 01背包 + 判断 #include <iostream> using namespace std; , MAXV = ; int v, n, m, f[MAX ...

  7. cordova+vue打包ios调用相机闪退解决

    cordova+vue项目打包android,打开相机正常使用,但是打包ios后,需要多几个配置,才能打开,否则当调用的时候会闪退,上配置图 需要在选中的文件里面添加 <key>NSCam ...

  8. 前端每日实战:48# 视频演示如何用纯 CSS 创作一盘传统蚊香

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/BVpvMz 可交互视频教程 此视频 ...

  9. 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(6.1)- FlexSPI NOR连接方式大全(RT600)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RT600的FlexSPI NOR启动的连接方式. 痞子衡前段时间一鼓作气写完了三篇关于i.MXRT1xxx系列Flex ...

  10. CORS(cross-origin-resource-sharing)跨源资源共享

    其实就是跨域请求.我们知道XHR只能访问同一个域中的资源,这是浏览器的安全策略所限制,但是开发中合理的跨域请求是必须的.CORS是W3的一个工作草案,基本思想就是:使用自定义的HTTP头部让浏览器与服 ...