Semaphore信号量深度解析
1. 使用指南
package com.multthread; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreTest {
//由于信号量是计数器递增,初始值可以随便设置
static volatile Semaphore sh = new Semaphore(2); public static void main(String[] args) throws InterruptedException { ExecutorService es = Executors.newFixedThreadPool(2);
// 将任务A加入线程池
es.submit(()->{
try {
System.out.println("t1...");
sh.release();
}catch (Exception e){}
});
// 将任务B加入线程池
es.submit(()->{
try {
System.out.println("t2...");
sh.release();
}catch (Exception e){}
}); // 等待子线程执行完release方法返回,注意这里release可以是同一个线程执行,只要调用了两次就行
// 此函数入参=当初始信号计数+调用次数时,才会放行,同时将计数器state重置为0
sh.acquire(4); // 将任务C加入到线程池
es.submit(()->{
try {
Thread.sleep(100);
System.out.println("t1...");
sh.release();
}catch (Exception e){}
}); // 将任务D加入到线程池
es.submit(()->{
try {
System.out.println("t2...");
sh.release();
}catch (Exception e){}
});
//由于state被重置为0了,所有所以这里入参写调用次数
sh.acquire(2);
System.out.println("main.....");
es.shutdown();
}
}
2.
基于AQS实现,与CountDownLatch不同的是,Semaphore内部的计数器是递增的。初始化的时候可以执行一个计数器的值,但是需要在需要同步的地方调用acquire方法执行需要同步的线程数。并且,内部的AQS实现(sync)获取信号量有公平策略和非公平策略之分。
3. 源码分析
- 构造函数
// 构造函数,默认采用非公平策略
public Semaphore(int permits) {
sync = new NonfairSync(permits);
} public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
- release函数
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// 尝试释放资源
if (tryReleaseShared(arg)) {
// 资源释放成功则调用park方法唤醒aqs队列里面最先挂起的线程
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
// CAS循环修改state值,直到修改成功
for (;;) {
// 获取当前的信号量值
int current = getState();
// 信号量值加releases,即+1
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count
exceeded");
// 使用CAS更新state的值
if (compareAndSetState(current, next))
return true;
}
}
// 释放资源完毕,调用唤醒挂起线程
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
- acquire方法
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取
if (tryAcquireShared(arg) < 0)
// 如果获取失败则加入到阻塞队列,然后再次尝试,如果失败则调用park方法挂起当前线程
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
Semaphore信号量深度解析的更多相关文章
- Feign Ribbon Hystrix 三者关系 | 史上最全, 深度解析
史上最全: Feign Ribbon Hystrix 三者关系 | 深度解析 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -25[ 博客园 总入口 ] 前言 疯狂创客圈(笔者尼恩创建的 ...
- Go netpoll I/O 多路复用构建原生网络模型之源码深度解析
导言 Go 基于 I/O multiplexing 和 goroutine 构建了一个简洁而高性能的原生网络模型(基于 Go 的I/O 多路复用 netpoll),提供了 goroutine-per- ...
- Java并发之Semaphore源码解析(二)
在上一章,我们学习了信号量(Semaphore)是如何请求许可证的,下面我们来看看要如何归还许可证. 可以看到当我们要归还许可证时,不论是调用release()或是release(int permit ...
- [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析
[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...
- 第37课 深度解析QMap与QHash
1. QMap深度解析 (1)QMap是一个以升序键顺序存储键值对的数据结构 ①QMap原型为 class QMap<K, T>模板 ②QMap中的键值对根据Key进行了排序 ③QMap中 ...
- Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN
http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...
- (转载)(收藏)OceanBase深度解析
一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...
- Kafka深度解析
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...
- java内存分配和String类型的深度解析
[尊重原创文章出自:http://my.oschina.net/xiaohui249/blog/170013] 摘要 从整体上介绍java内存的概念.构成以及分配机制,在此基础上深度解析java中的S ...
随机推荐
- leetcode 练习--反转链表
最近开始学习数据结构和算法的学习,也自然开始在 leetcode 上练习,所以每周大概会分享做过的leetcode 练习,尽量做到每天更新一道题目. 作为 leetcode 练习笔记的第一道题目,选择 ...
- 区块链V1版本实现之四
部分程序代码(添加区块): //添加区块 func (bc *BlockChain) AddBlock(data string) { //创建一个区块 //bc.Block的最后一个区块的Hash值就 ...
- select监听服务端
# can_read, can_write, _ = select.select(inputs, outputs, None, None)## 第一个参数是我们需要监听可读的套接字, 第二个参数是我们 ...
- Appium之测试微信小程序
坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:Oschina.云+社区.知乎等各大平台都有. 目录 一.往期回顾 二.测试微信小程序 1.准备工作 2.操作步骤 3.注意 4.强制设置安卓的进程 ...
- 【NOIP2017提高A组模拟9.17】信仰是为了虚无之人
[NOIP2017提高A组模拟9.17]信仰是为了虚无之人 Description Input Output Sample Input 3 3 0 1 1 7 1 1 6 1 3 2 Sample O ...
- Python中使用“模块名.__all__”查看模块建议导出的属性
在<第10.5节 使用__all__定义Python模块导入白名单>中,老猿介绍了在自定义模块中使用定义__all__属性来提供模块对外可见的白名单,使用该机制除了可以定义访问的白名单外, ...
- PyQt学习随笔:Model/View开发时在view数据项中设置不同角色数据的方法
在往Model中通过QStandardItem等类插入数据项时,除了实际插入的存储数据,还可以设置不同角色(请参考<PyQt学习随笔:Model/View中诸如DisplayRole的数据角色及 ...
- RedHat-Linux操作指令第1篇
不同的linux系统切换方式会稍有一点差别 从图形界面切换到字符界面:Alt+F(1-8) 或者 Alt+Ctrl+Shift+F(1-8) 从字符界面切换回图形界面:Alt+F7 字符界面启动到图形 ...
- c++如何按照map的value进行排序?
static bool cmp(pair<char, int> a , pair<char,int> b) { return a.second>b.second; //按 ...
- 个人作业三——ATM管理系统
一 作业信息 博客班级 https://edu.cnblogs.com/campus/ahgc/AHPU-se-JSJ18/ 作业要求 https://edu.cnblogs.com/campus/a ...