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 ...
随机推荐
- C语言讲义——内联函数
如果一些函数被频繁调用,不断地有函数入栈(Stack),会造成栈空间的大量消耗. 对应这种问题,可以使用内联函数(inline). 编译器会将内联函数的代码整段插入到调用的位置. #include & ...
- Java蓝桥杯练习题——求小数n位后3个数
求整数除法小数点后第n位开始的3位数 位数不足的补0,如0.125小数第3位后三位:0.12500→500 输入格式:a b n,空格分开,a是被除数,b是除数,n是小数后的位置 输出格式:3位数字, ...
- dubbo起停之服务消费
ReferenceAnnotationBeanPostProcessor继承了AnnotationInjectedBeanPostProcessors其实现了InstantiationAwareBea ...
- Elasticsearch基本CURD操作语法讲解
当我们的ES集群搭建完成以后,我怎么能看到集群中各个节点状态以及主节点和健康情况呢,如下讲解使用curl命令来与ES集群进行交互.分别有查询主节点情况.集群状态.以及创建索引查看索引.查看分片以及对E ...
- 第7.12节 可共享的Python类变量
第7.12节 可共享的Python类变量 一. 引言 在上节已经引入介绍了类变量和实例变量,类体中定义的变量为类变量,默认属于类本身,实例变量是实例方法中定义的self对象的变量,对于每个实例都 ...
- PyQt(Python+Qt)学习随笔:Designer中的QDialogButtonBox的accepted、rejected和helpRequested信号
QDialogButtonBox中可以包含多个pushButton,但QDialogButtonBox本身只提供4种信号,分别是accepted.rejected.clicked和helpReques ...
- 关于RequestParam在不同的Spring版本上,接口在controller重载时注解可能失效的踩坑记录
先抛背景: 我项目中的Spring版本是2.0.3.RELEASE. api-demo负责暴露接口,service-demo负责实现功能.接口参数的@RequestParam和@RequestBody ...
- Get请求Test
一.新建测试套 作为管理接口,可按功能分类,也可按业务逻辑分类,根目录下最多一级子目录.运行接口时,可按测试套为单位,整体运行. 二.选择请求类型,输入接口地址 根据接口文档中提供的接口请求类型及地址 ...
- [GKCTF2020]cve版签到
cve-2020-7066漏洞 利用get_header($url)函数漏洞%00对部分url截断 构造ssrf请求,用127.0.0.1网址访问目标主机内部资源 其实就是get_header()的C ...
- Nmap学习使用指南
本博客严重参考 Nmap使用指南1.0: https://github.com/scanfsec/penetration/blob/master/Nmap%E4%BD%BF%E7%94%A8%E6%8 ...