前言

本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和参考文献请见100个问题搞定Java并发

正文

信号量为多线程协作提供了更为强大的控制方法。

从广义上说,信号量是对锁的扩展。

无论是内部锁 synchronized 还是重入锁 ReentrantLock ,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。

信号量主要提供了以下构造函数

public Semaphore(int permits)public Semaphore(int permits,boolean fair)  //第二个参数可以指定是否公平在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。

当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。

信号量的主要逻辑方法有:

public void acquire()throws InterruptedExceptionpublic void acquireUninterruptibly()public boolean tryAcquire()public boolean tryAcquire(long timeout,TimeUnit unit)throws InterruptedExceptionpublic void release()

方法尝试获得一个准入的许可。

若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。

acquireUninterruptibly() 方法和 acquire() 方法类似,但是不响应中断。

tryAcquire() 方法尝试获得一个许可,如果成功则返回 true ,失败则返回 false ,它不会进行等待,立即返回。

release() 方法用于在线程访问资源结東后释放一个许可,以使其他等待许可的线程可以进行资源访问。

源码(JDK8)

/*** 一个计数信号量。 ** 在概念上,信号量维持一组许可证。 ** 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 ** 每个release()添加许可证,潜在地释放阻塞获取方。 ** 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。** 信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。** 在获得项目之前,每个线程必须从信号量获取许可证,以确保某个项目可用。 ** 当线程完成该项目后,它将返回到池中,并将许可证返回到信号量,允许另一个线程获取该项目。** 请注意,当调用acquire()时,不会保持同步锁定,因为这将阻止某个项目返回到池中。 ** 信号量封装了限制对池的访问所需的同步,与保持池本身一致性所需的任何同步分开。** 信号量被初始化为一个,并且被使用,使得它只有至多一个允许可用,可以用作互斥锁。 ** 这通常被称为二进制信号量 ,因为它只有两个状态:一个许可证可用,或零个许可证可用。 ** 当以这种方式使用时,二进制信号量具有属性(与许多Lock实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权概念)。 ** 这在某些专门的上下文中是有用的,例如死锁恢复。** 此类的构造函数可选择接受公平参数。 ** 当设置为false时,此类不会保证线程获取许可的顺序。 ** 特别是, 闯入是允许的,也就是说,一个线程调用acquire()可以提前已经等待线程分配的许可证-在等待线程队列的头部逻辑新的线程将自己。 ** 当公平设置为真时,信号量保证调用acquire方法的线程被选择以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。 ** 请注意,FIFO排序必须适用于这些方法中的特定内部执行点。 ** 因此,一个线程可以在另一个线程之前调用acquire ,但是在另一个线程之后到达排序点,并且类似地从方法返回。 ** 另请注意, 未定义的tryAcquire方法不符合公平性设置,但将采取任何可用的许可证。** 通常,用于控制资源访问的信号量应该被公平地初始化,以确保线程没有被访问资源。 ** 当使用信号量进行其他类型的同步控制时,非正常排序的吞吐量优势往往超过公平性。** 这个类还提供了方便的方法, 一次acquire和release多个许可证。 ** 当没有公平地使用这些方法时,请注意增加无限期延期的风险。** 内存一致性效应:在一个线程中调用“释放”方法的操作,例如release() happen-before 在另一个线程中成功执行“获取”方法 如 acquire() 。*/
public class Semaphore implements java.io.Serializable

官方示例

package com.shockang.study.java.concurrent.aqs;import java.util.concurrent.Semaphore;/*** 这是一个使用信号量来控制对一个项目池的访问的类*/
public class Pool {private static final int MAX_AVAILABLE = 100;private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);public Object getItem() throws InterruptedException {available.acquire();return getNextAvailableItem();}public void putItem(Object x) {if (markAsUnused(x))available.release();}protected Object[] items = {};protected boolean[] used = new boolean[MAX_AVAILABLE];protected synchronized Object getNextAvailableItem() {for (int i = 0; i < MAX_AVAILABLE; ++i) {if (!used[i]) {used[i] = true;return items[i];}}return null; // not reached}protected synchronized boolean markAsUnused(Object item) {for (int i = 0; i < MAX_AVAILABLE; ++i) {if (item == items[i]) {if (used[i]) {used[i] = false;return true;} elsereturn false;}}return false;}
}

实践

简单入门示例

package com.shockang.study.java.concurrent.aqs;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;public class SemaphoreDemo implements Runnable {final Semaphore semp = new Semaphore(5);@Overridepublic void run() {try {semp.acquire();Thread.sleep(2000);System.out.println(Thread.currentThread().getId() + ":done!");} catch (InterruptedException e) {e.printStackTrace();} finally {semp.release();}}public static void main(String[] args) {ExecutorService exec = Executors.newFixedThreadPool(20);final SemaphoreDemo demo = new SemaphoreDemo();for (int i = 0; i < 20; i++) {exec.submit(demo);}}
}

说明

第8行代码声明了一个包含5个许可的信号量。

这就意味着同时可以有5个线程进入代码段第13~15行。

第13~15行为临界区管理代码,程序会限制执行这段代码的线程数。

申请信号量使用 acquire方法操作,在离开时,务必使用 release方法释放信号量(代码第 19 行)。

这就和释放锁是一个道理。

如果不幸发生了信号量的泄露(申请了但没有释放), 那么可以进入临界区的线程数量就会越来越少,直到所有的线程均不可访问。

在本例中, 同时开启20个线程。观察这段程序的输出,你就会发现系统以5个线程一组为单位,依次输出带有线程 ID 的提示文本。

Semaphore 是什么?怎么用?相关推荐

  1. Java并发编程之CountDownLatch、CyclicBarrier和Semaphore

    前言 本文为对CountDownLatch.CyclicBarrier.Semaphore的整理使用 CountDownLatch CountDownLatch类位于java.util.concurr ...

  2. java 多线程 信号_Java多线程——Semaphore信号灯

    Semaphore [ˈseməfɔːr]可以维护当前访问自身的线程个数,并提供了同步机制.使用Semaphore可以控制同时访问资源的线程个数(即允许n个任务同时访问这个资源),例如,实现一个文件允 ...

  3. linux内核 semaphore,2.4内核里semaphore源码的一个疑问

    博主你好, 请教一个问题. __down()里面有一段代码,  我觉得不那么保险.我先把__down的源码贴出来: ========================================== ...

  4. JAVA中的并发工具 -- CountDownLatch、CyclicBarrier、Semaphore

    2019独角兽企业重金招聘Python工程师标准>>> CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatc ...

  5. 信号量Semaphore一篇文章叫你明白

    已经习惯了阿里面试官的冷笑:用过Semaphore吧,不妨说说? 本质就是 信号量模型,模型图如下: 其中的 计数器 和 等待队列 对外部是透明的,仅能通过提供的三大方法访问它们. 详细说说哪三大方法 ...

  6. 别再和面试官说不懂信号量Semaphore了!

    已经习惯了阿里面试官的冷笑:用过Semaphore吧,不妨说说? 本质就是 信号量模型,模型图如下: 其中的 计数器 和 等待队列 对外部是透明的,仅能通过提供的三大方法访问它们. 详细说说哪三大方法 ...

  7. 15.并发工具类(解析hashtable,ConcurrentHashMap1.7与1.8的区别以及Semaphore)

    3. 并发工具类 3.1 并发工具类-Hashtable Hashtable出现的原因:在集合类中HashMap是比较常用的集合对象,但是HashMap在多线程环境下可能会出现线程不安全的情况,为了保 ...

  8. LeetCode 1115. Print FooBar Alternately--多线程并发问题--Java解法--CyclicBarrier, synchronized, Semaphore 信号量

    此文首发于我的个人博客:zhang0peter的个人博客 LeetCode题解专栏:LeetCode题解 LeetCode 所有题目总结:LeetCode 所有题目总结 题目地址:Print FooB ...

  9. Semaphore(信号量)

    Semaphore是什么 Semaphore通常我们叫它信号量,可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源. 使用场景 通常用于哪些资源有明确访问数量限制的场景,常 ...

  10. Redisson 分布式锁源码 11:Semaphore 和 CountDownLatch

    前言 Redisson 除了提供了分布式锁之外,还额外提供了同步组件,Semaphore 和 CountDownLatch. Semaphore 意思就是在分布式场景下,只有 3 个凭证,也就意味着同 ...

最新文章

  1. PHP学习笔记十九【析构函数】
  2. php的4种常用运行方式
  3. Javascript:getElementsByClassName
  4. 零基础学python实战-Python3.6零基础入门与实战 PDF 带源码视频版
  5. 有shi以来最详细的正则表达式入门教程
  6. Android关于Paint你所知道的和不知道的一切
  7. php memcache 缓存与memcached 客户端的详细步骤
  8. 用matlab模拟3电荷,基于Matlab模拟点电荷电场线和等势线
  9. 小七揭密delphi源码免杀系列教程
  10. uCharts 图表
  11. 获取图片的十六进制颜色码(keynote/ppt取色器)
  12. 以核心素养为导向的计算机教学方式,“雨花学术论坛”再开讲:让核心素养在课堂“落地生根”...
  13. C/C++错误分析errno,perror,strerror和GetLastError()函数返回的错误代码的意义
  14. 爱科赛博冲刺科创板:拟募资3.8亿 陕西集成电路与达晨是股东
  15. Android开发 设置手机壁纸
  16. Improving description-based person-identification by multi-granularity image-text alignments-2019TIP
  17. 曝iPhone15或换用USB-C接口;Google将下架第三方Android通话录音APP|极客头条
  18. Jetson Nano官方预训练模型百度网盘下载链接分享
  19. 【Bootstrap学习笔记】12.轮播插件
  20. GPO:在 Start/Logon 中使用 PowerShell 脚本

热门文章

  1. C++基础学习DAY1-02 双冒号作用域
  2. ios 国际化设置及默认语言设置
  3. 写给产品运营等非技术同学的SQL教程
  4. shell实现android手机反复重启
  5. SDN痴心不改,云杉网络完成B+轮1100万美元融资
  6. html盒子代码div6,WEB入门.六 盒子模型
  7. python量化交易通达信_通达信交易策略,通达信量化交易
  8. PHP入门基础与实战技巧
  9. 苹果手机变成耳机模式怎么调回来_苹果耳机推出“新功能”:打僵尸!
  10. win10+Ubuntu双系统 Easybcd安装