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信号量深度解析的更多相关文章

  1. Feign Ribbon Hystrix 三者关系 | 史上最全, 深度解析

    史上最全: Feign Ribbon Hystrix 三者关系 | 深度解析 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -25[ 博客园 总入口 ] 前言 疯狂创客圈(笔者尼恩创建的 ...

  2. Go netpoll I/O 多路复用构建原生网络模型之源码深度解析

    导言 Go 基于 I/O multiplexing 和 goroutine 构建了一个简洁而高性能的原生网络模型(基于 Go 的I/O 多路复用 netpoll),提供了 goroutine-per- ...

  3. Java并发之Semaphore源码解析(二)

    在上一章,我们学习了信号量(Semaphore)是如何请求许可证的,下面我们来看看要如何归还许可证. 可以看到当我们要归还许可证时,不论是调用release()或是release(int permit ...

  4. [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

    [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...

  5. 第37课 深度解析QMap与QHash

    1. QMap深度解析 (1)QMap是一个以升序键顺序存储键值对的数据结构 ①QMap原型为 class QMap<K, T>模板 ②QMap中的键值对根据Key进行了排序 ③QMap中 ...

  6. Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN

    http://m.blog.csdn.net/blog/wu010555688/24487301 本文整理了网上几位大牛的博客,详细地讲解了CNN的基础结构与核心思想,欢迎交流. [1]Deep le ...

  7. (转载)(收藏)OceanBase深度解析

    一.OceanBase不需要高可靠服务器和高端存储 OceanBase是关系型数据库,包含内核+OceanBase云平台(OCP).与传统关系型数据库相比,最大的不同点, 是OceanBase是分布式 ...

  8. Kafka深度解析

    本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅 ...

  9. java内存分配和String类型的深度解析

    [尊重原创文章出自:http://my.oschina.net/xiaohui249/blog/170013] 摘要 从整体上介绍java内存的概念.构成以及分配机制,在此基础上深度解析java中的S ...

随机推荐

  1. Ajax原理与图解

    Ajax原理 Ajax 的全称是Asynchronous JavaScript and XML. Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后 ...

  2. python删除list中的空list

    list1 = [[], [], [], [], [], 'text', 'text2', [], 'moreText'] 如何删除空列表,以便我得到: list2 = ['text', 'text2 ...

  3. Integer中的奇妙位运算

    Integer中的奇妙位运算 参考资料 https://segmentfault.com/a/1190000015763941 highestOneBit(int i) 函数的作用是获得传入参数的最高 ...

  4. Java 基础之 String 类

    String String 被声明为 final,因此不能被继承.(Integer 等包装类也不能被继承) 在 java8 中,String 内部使用 char 数组 来存储数据 public fin ...

  5. python核心高级学习总结8------动态性、__slots__、生成器、迭代器、装饰、闭包

    python的动态性 什么是动态性呢,简单地来说就是可以在运行时可以改变其结构,如:新的函数.对象.代码都可以被引进或者修改,除了Python外,还有Ruby.PHP.javascript等也是动态语 ...

  6. PyQt(Python+Qt)学习随笔:QDockWidget停靠部件的dockWidgetArea和docked属性

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 dockWidgetArea和docked属性这两个属性在Design ...

  7. 第二十九章、containers容器类部件QFrame框架部件详解

    一.概述 容器部件就是可以在部件内放置其他部件的部件,在Qt Designer中可以使用的容器部件有如下: 容器中的Frame为一个矩形的框架对象,对应类QFrame,QFrame类是PyQt中带框架 ...

  8. CobaltStrike 生成office宏病毒进行钓鱼攻击

    关于WORD宏: 在百度百科上有: 宏是一个批量处理程序命令,正确地运用它可以提高工作效率.微软的office软件允许用户自己编写,叫VBA的脚本来增加其灵活性,进一步扩充它的能力.如完打开word文 ...

  9. 云服务器AWD平台搭建

    开学后实验室来了几个新同学,在线上CTF方面大家一直在持续学习,但AWD模式的CTF我们练习并不多,所以准备搭建一个AWD平台用于实验室成员的线下赛攻防练习. 最开始的是防灾科技大学的线下AWD靶场: ...

  10. 测试window安装的客户端

    1.win10 安装了客户端,测试一下,