AQS系列(六)- Semaphore的使用及原理
前言
Semaphore也是JUC包中一个用于并发控制的工具类,举个常用场景的例子:有三台电脑五个人,每个人都要用电脑注册一个自己的账户,这时最开始只能同时有三个人操作电脑注册账户,这三个人中有人操作完了剩下的两个人才能占用电脑注册自己的账户。这就是Semaphore的经典使用场景,跟并发加锁有点像,只是我们的并发加锁同一时间只让有一个线程执行,而Semaphore的加锁控制是允许同一时间有指定数量的线程同时执行,超过这个数量就加锁控制。
一、使用样例
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // 对比上面例子中的3台电脑
for (int i = 0; i < 5; i++) { // 对比上面例子中的5个人
new Thread(() -> {
try {
semaphore.acquire(1); // 注意acquire中的值可以传任意值>=0的整数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " acquire 1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "release 1");
semaphore.release(1);
}).start();
}
}
执行结果为:
Thread-0 acquire 1
Thread-2 acquire 1
Thread-1 acquire 1
Thread-1release 1
Thread-2release 1
Thread-0release 1
Thread-4 acquire 1
Thread-3 acquire 1
Thread-4release 1
Thread-3release 1
可以看到同一时间只有三个线程获取到了锁,这三个执行完释放了之后,剩下两个菜获取锁执行。下面看看源码是如何实现的。
二、源码实现
1、Semaphore构造器
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
可以看到,Semaphore有两个构造器,一个是只传数值默认非公平锁,另一个可指定用公平锁还是非公平锁。permits最终还是赋值给了AQS中的state变量。
2、acquire(1)方法
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
此方法同样调用了AQS中的模板方法:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
1)、查看tryAcquireShared的实现方法
先看非公平锁的获取:
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires; // 如果remaining是负的,说明当前剩余的信号量不够了,需要阻塞
if (remaining < 0 ||
compareAndSetState(available, remaining)) // 如果remaining<0则直接return,不会走CAS;如果大于0,说明信号量还够,可走CAS将信号量减掉,成功则返回大于0的remaining
return remaining;
}
}
再看公平锁的获取:
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors()) // 判断是不是在队首,不是的话直接返回-1
return -1;
int available = getState(); // 后面逻辑通非公平锁的获取逻辑
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
可以看到,不管非公平锁和公平锁,加锁时都是先判断当前state够不够减的,如果减出负数返回获取锁失败,是正数才走CAS将原信号量扣掉,返回获取锁成功。加锁时一个减state的过程。
2)、doAcquireSharedInterruptibly
此方法还是AQS中的实现,逻辑重复,就不再说明了。
3、release(1)方法
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
同样调用了AQS中的模板方法releaseShared:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
其中tryReleaseShared的实现在Semaphore类的Sync中,如下所示:
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases; // 用当前state加上要释放的releases
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) // 用CAS将state加上
return true;
}
}
另一个方法doReleaseShared之前看过,此处就不赘述了。
三、小结
Semaphore信号量类基于AQS的共享锁实现,有公平锁和非公平锁两个版本。它的加锁与释放锁的不同之处在于和普通的加锁释放锁反着,ReentrantLock和ReentrantReadWriteLock中都是加锁时state+1,释放锁时state-1,而Semaphore中是加锁时state减,释放锁时state加。
另外,如果它还可以acquire(2) 、release(1),即获取的和释放的信号量可以不一致,只是需要注意别释放的信号量太少导致后续任务获取不到足够的量而永久阻塞。
AQS系列(六)- Semaphore的使用及原理的更多相关文章
- java多线程系列(六)---线程池原理及其使用
线程池 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知 ...
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
- java基础解析系列(六)---注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java并发编程系列-(8) JMM和底层实现原理
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...
- AQS系列(七)- 终篇:AQS总结
前言 本文是对之前AQS系列文章的一个小结,首先看看以下几个问题: 1.ReentrantLock和ReentrantReadWriteLock的可重入特性是如何实现的? 2.哪个变量控制着锁是否被占 ...
- CountDownLatch、CyclicBarrier和Semaphore 使用示例及原理
备注:博客园的markDown格式支持的特别不友好.也欢迎查看我的csdn的此篇文章链接:CountDownLatch.CyclicBarrier和Semaphore 使用示例及原理 CountDow ...
- Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图
Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图 Bing Maps Silverlight Control虽然为我们提供了简洁.方面的开 ...
- AQS系列(一)- ReentrantLock的加锁
前言 AQS即AbstractQueuedSynchronizer,是JUC包中的一个核心抽象类,JUC包中的绝大多数功能都是直接或间接通过它来实现的.本文是AQS系列的第一篇,后面会持续更新多篇,争 ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
随机推荐
- 详解 PHP 中的三大经典模式
单例模式 单例模式的含义: 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 单例模式 ...
- Res2net:多尺度骨干网络结构
<Res2Net: A New Multi-scale Backbone Architecture> 来自:南开大学程明明组 论文:https://arxiv.org/abs/1904.0 ...
- 内核升级在线安装报错:Could not retrieve mirrorlist http://mirrors.elrepo.org/mirrors-elrepo-kernel.el7 error was14: curl#6 - "Could not resolve host: mirrors.elrepo.org; 未知的错误"
修改网卡配置 [root@localhost ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens32 BOOTPROTO="none" ...
- Xftp工具连接 报错: 无法与 “ 目标IP ” 连接
这里是OpenSSH升级导致 查看sshd_config [root@sdw1 glibc]# vim /etc/ssh/sshd_config 查找后进行修改 [root@sdw1 glibc]# ...
- LESSON 1-Introduction
Keywords: Communication system, Channel model, Channel capacity by Shannon 1. Two fundamental archit ...
- redis与memcached区别
不同点: (1) redis中并不是所有数据在有效期内只能常驻内存的(如果需要,可定期同步持久化到磁盘),这是和memcached相比一个最大的区别(memcached中的数据在有效期内是以键值对的形 ...
- fsockopen以Socket方式模拟HTTP下载文件
fsockopen 的功能很强大,比如前面模拟 HTTP 访问,模拟 POST/GET 请求,什么的,这里再举一个例子,那就是下载东西.比如下载 http://www.nowamagic.net//l ...
- 华为云Volcano:让企业AI算力像火山一样爆发
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
- php权重分配
假设有3个人 能力的权重 分别为 A=>1,B=>2,C=>3,那么当有6个案子的时候 A分配到1个,B分配到2个,C分配到3个,这很合理,但是当案子只有5个,或者有7个的时候, ...
- CodeForces985G Team Players
G. Team Players time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...