Semaphore可以理解为信号量,用于控制资源能够被并发访问的线程数量,以保证多个线程能够合理的使用特定资源。Semaphore就相当于一个许可证,线程需要先通过acquire()方法获取该许可证,该线程才能继续往下执行,否则只能在该方法出阻塞等待。当执行完业务功能后,需要通过release()方法将许可证归还,以便其他线程能够获得许可证继续执行。

文章目录

  • Semaphore简介
  • Semaphore使用场景
  • Semaphore方法
  • Semaphore实例
  • Semaphore源码分析
  • 本文小结

Semaphore简介

Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。


Semaphore使用场景

适用于那些资源有明确访问数量限制的场景,常用于限流 。

比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。

比如:停车场场景,车位数量有限,同时只能容纳多少台车,车位满了之后只有等里面的车离开停车场外面的车才可以进入。


Semaphore方法

Semaphore方法

常用和需要注意的方法

acquire() 获取一个许可
acquire(int permits) 获取指定个数的许可
tryAcquire()方法尝试获取1个许可证
tryAcquire(long timeout, TimeUnit unit) 最大等待许可的时间
tryAcquire(int permits) 获取指定个数的许可
tryAcquire(int permits, long timeout, TimeUnit unit) 最大等待许可的时间
availablePermits() : 返回此信号量中当前可用的许可证数
release() 释放许可
release(int permits) 释放指定个数的许可
int getQueueLength() 返回正在等待获取许可证的线程数。
boolean hasQueuedThreads() 是否有线程正在等待获取许可证。
void reducePermits(int reduction) 减少reduction个许可证。是个protected方法。
Collection getQueuedThreads() 返回所有等待获取许可证的线程集合。是个protected方法。

Semaphore实例

代码实现

package cn.wideth.util;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;public class MySemaphore {private final static int threadCount = 20;public static void main(String[] args){ExecutorService es = Executors.newCachedThreadPool();//表示允许3个线程获得许可证,也就是最大并发数目为3final Semaphore semaphore = new Semaphore(3);for(int i = 0; i < threadCount; i++){final int threadNum = i;es.execute(() -> {try {semaphore.acquire();//获得一个许可printNumber(threadNum);semaphore.release();//释放一个许可} catch (InterruptedException e) {e.printStackTrace();}});}es.shutdown();}private static void printNumber(int threadNum) {System.out.println(Thread.currentThread().getName() + "-->" + threadNum);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}
}

运行结果

代码分析

从这个例子就可以看出,Semaphore用来做特殊资源的并发访问控制是相当合适的,如果有业务场景需要进行流量控制,可以优先考虑Semaphore


Semaphore源码分析

此前你需要掌握AQS原理,以及CAS

import java.util.Collection;
import java.util.concurrent.TimeUnit;public class Semaphore implements java.io.Serializable {private static final long serialVersionUID = -3222578661600680210L; // 序列化版本号private final Sync sync; // 抽象队列同步器的子类成员常量// 构造方法/*** 构造一个有permits个信号量的非公平同步器*/public Semaphore(int permits) {sync = new NonfairSync(permits);}/*** 构造一个有permits个信号量* 如果传入的布尔值fair=true则是公平同步器,false则是非公平同步器*/public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);}/*** 下面是常用方法*//*** 获取信号量*/// 获取一个信号量,如果信号量被使用完则抛异常InterruptedExceptionpublic void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);}// 获取permits个信号量,如果不够则抛异常InterruptedExceptionpublic void acquire(int permits) throws InterruptedException {if (permits < 0) throw new IllegalArgumentException();sync.acquireSharedInterruptibly(permits);}// 获取permits个不可中断的信号量public void acquireUninterruptibly(int permits) {if (permits < 0) throw new IllegalArgumentException();sync.acquireShared(permits);}// 阻塞式(就是等待)获取信号量,当其它信号量被释放,并执行当前代码则成功public void acquireUninterruptibly() {sync.acquireShared(1);}/*** 释放信号量方法*/// 释放一个信号量public void release() {sync.releaseShared(1);}// 释放permits个信号量public void release(int permits) {if (permits < 0) throw new IllegalArgumentException();sync.releaseShared(permits);}/*** 常用信号量辅助方法*/// 获取所有信号量,并将所有信号量都使用掉,返回0public int drainPermits() {return sync.drainPermits();}// 获取可用的信号量个数public int availablePermits() {return sync.getPermits();}// 获取等待线程队列的长度public final int getQueueLength() {return sync.getQueueLength();}/*** 判断信号量方法*/// 判断是否可以获取一个信号量public boolean tryAcquire() {return sync.nonfairTryAcquireShared(1) >= 0;}// 判断是否可以获取permits个信号量public boolean tryAcquire(int permits) {if (permits < 0) throw new IllegalArgumentException();return sync.nonfairTryAcquireShared(permits) >= 0;}// 判断在指定时间内可否获取一个信号量,超过等待时间timeout则抛异常InterruptedExceptionpublic boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}// 判断在timeout时间内是否可以获取permits个信号量throws InterruptedExceptionpublic boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {if (permits < 0) throw new IllegalArgumentException();return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));}// 判断是否是公平机制的信号同步器public boolean isFair() {return sync instanceof FairSync;}// 判断线程队列是否有线程,有线程在等待获取信号量则返回true,否则返回falsepublic final boolean hasQueuedThreads() {return sync.hasQueuedThreads();}/*** toString方法得到信号量*/public String toString() {return super.toString() + "[Permits = " + sync.getPermits() + "]";}/*** 获取线程队列的集合*/protected Collection<Thread> getQueuedThreads() {return sync.getQueuedThreads();}// 减少reduction个信号量(总量减少)protected void reducePermits(int reduction) {if (reduction < 0) throw new IllegalArgumentException();sync.reducePermits(reduction);}/*** 同步器的抽象方法*/abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 1192457210091910933L;  // 序列化版本号// 带一个参数的构造方法Sync(int permits) {setState(permits);// 调用AQS中的setState}// 获得许可数量final int getPermits() {return getState();//调用AQS的方法}// 使用acquires个信号量,失败则直接返回计算得到的剩余信号量的值final int nonfairTryAcquireShared(int acquires) {for (; ; ) {int available = getState();//不断自旋获取当前剩余信号量int remaining = available - acquires;//计算剩余信号量//前面如果false则需要执行后面的cas操作更新信号量,如果剩余信号量大于0则将这个负值的信号量直接返回if (remaining < 0 || compareAndSetState(available, remaining))return remaining;}}// 释放releases个信号量 失败则会抛异常protected final boolean tryReleaseShared(int releases) {for (; ; ) {int current = getState();// 不断自旋获取当前许可值int next = current + releases;// 计算释放后的新许可值if (next < current) // 超过了最大值(构造的时候传入的值)需要抛异常throw new Error("Maximum permit count exceeded");if (compareAndSetState(current, next))// 说明没有超过最大值,进行更新许可值,并返回truereturn true;}}// 使用reductions个信号量减少的是总量,也就是最大值final void reducePermits(int reductions) {for (; ; ) {int current = getState(); // 当前许可int next = current - reductions;// 计算减少后的新的总量if (next > current) // 新的总量不能比之前的总量大,大则抛异常throw new Error("Permit count underflow");if (compareAndSetState(current, next))return;}}final int drainPermits() {for (; ; ) {int current = getState();if (current == 0 || compareAndSetState(current, 0)) // 将信号量更新为0,表示全部使用完了return current;}}}/*** 实现同步器的公平同步器*/static final class FairSync extends Sync {private static final long serialVersionUID = 2014338818796000944L;FairSync(int permits) {super(permits);}//尝试获取信号量(公平锁机制)protected int tryAcquireShared(int acquires) {for (; ; ) {if (hasQueuedPredecessors()) //如果已经形成了AQS队列,说明信号量早用完了,直接返回-1return -1;int available = getState();  //当前可用信号量int remaining = available - acquires;// 计算使用了acquires信号量的值if (remaining < 0 || compareAndSetState(available, remaining))// 值小于0直接返回计算后的负值remaining,不小于0则说明不会进行阻塞return remaining;}}}/*** 实现同步器的非公平同步器*/static final class NonfairSync extends Sync {private static final long serialVersionUID = -2694183684443567898L;NonfairSync(int permits) {super(permits);}//尝试获取信号量(非公平锁机制)protected int tryAcquireShared(int acquires) {return nonfairTryAcquireShared(acquires); // 调用父类Sync种的final方法}}}

本文小结

本文详细描述了Semaphore信号量的基本概念,常用方法,最后对源码进行了分析。

同步工具之Semaphore信号量相关推荐

  1. java semaphore 等待时间_一个java同步工具类Semaphore的详解

    Semaphore是java并发包里面的一个工具类,我们限制可以访问某些资源的线程数目就可以使用Semaphore了.这篇文章将对Semaphore的概念和使用进行一个详解. 一.概念理解 官方是这样 ...

  2. Java并发编程实战————Semaphore信号量的使用浅析

    引言 本篇博客讲解<Java并发编程实战>中的同步工具类:信号量 的使用和理解. 从概念.含义入手,突出重点,配以代码实例及讲解,并以生活中的案例做类比加强记忆. 什么是信号量 Java中 ...

  3. java并发基础(二)--- Java监视器模型、并发容器、同步工具类(闭锁、信号量)

    原blog链接:http://www.cnblogs.com/peterxiao/p/6921903.html 总结一下第4.5章的东西. 一.java监视器模式 概念:把对象的所有可变状态都封装起来 ...

  4. Java高并发编程:同步工具类

    内容摘要 这里主要介绍了java5中线程锁技术以外的其他同步工具,首先介绍Semaphore:一个计数信号量.用于控制同时访问资源的线程个数,CyclicBarrier同步辅助类:从字面意思看是路障, ...

  5. Linux 信号量 源码,一文读懂go中semaphore(信号量)源码

    运行时信号量机制 semaphore 前言 最近在看源码,发现好多地方用到了这个semaphore. 本文是在go version go1.13.15 darwin/amd64上进行的 作用是什么 下 ...

  6. Java并发中常用同步工具类

    为什么80%的码农都做不了架构师?>>>    同步工具类可以是任何一个对象,只要它根据其自身的状态来协调线程控制流.阻塞队列(BlockingQueue)可以作为同步工具类,其他类 ...

  7. 将指定的计数添加到信号量中会导致其超过_并发编程用不上?Semaphore信号量了解一下...

    什么是信号量 Java中的同步工具类信号量即计数信号量(Counting Semaphore),是用来控制访问某个特定资源的操作数量,或同时执行某个指定操作的数量.可以简单理解为信号量用来限制对某个资 ...

  8. 深入分析同步工具类之AbstractQueuedSynchronizer

    概览: AQS(简称)依赖内部维护的一个FIFO(先进先出)队列,可以很好的实现阻塞.同步:volatile修饰的属性state,哪个线程先改变这个状态值,那么这个线程就获得了优先权,可以做任何事(当 ...

  9. (三)线程同步工具集_1---控制线程并发访问一个资源

    2019独角兽企业重金招聘Python工程师标准>>> 线程同步工具集 在前面了解了线程的同步机制,临界区等,了解了线程的两种基本的同步机制: synchronized关键字: Lo ...

最新文章

  1. BlueZ--内核层+应用层
  2. c java 开发android_java代码与纯C代码混编完成android应用的开发
  3. 长征五号复飞成功:史上最重最大,2020月岩采样火星探测都要靠它
  4. 安卓 java编译_Android源码分析(七)-----如何解决java编译版本问题
  5. (转载)Android两种Tab分页的方式:TabActivity和ActivityGroup以及Android项目几种常见的应用架构...
  6. Cisco堆叠配置步骤+链路聚合实例
  7. 英国电信移除华为设备后,多家运营商继续与华为合作,并达成20亿英镑协议...
  8. 用iframe框架,登录过期后登录框在框架里加载的问题
  9. VideoPlayer参数
  10. Ubuntu/Linux/Unix 究竟有什么区别??
  11. 用C语言数组编写贪吃蛇
  12. Deeping Learning学习与感悟——《深度学习工程师》_2
  13. MySQL 批量插入申请自增 ID
  14. 人体的神经系统图 分布,人体脑神经系统分布图
  15. 锐捷交换机查询端口对应的IP,IP对应的端口
  16. 阿里云服务器的购买以及使用
  17. c++ 二进制文件读写
  18. SpringMVC框架中@ControllerAdvice和对应Advice切面使用原理
  19. ubuntu mysql密码忘记了怎么办,ubuntu怎么查看mysql密码
  20. 2015移动安全病毒年报

热门文章

  1. Tair是一个高性能,分布式,可扩展,高可靠的key/value结构存储系统(转)
  2. MyBatis学习总结(13)——Mybatis查询之resultMap和resultType区别
  3. Hexo Reload in new Mac
  4. [Linux] PHP程序员玩转Linux系列-备份还原MySQL
  5. postgresql数据类型之数组类型
  6. 安装完补丁后是否需要服务器重新启动
  7. Redis-过期Key删除/淘汰Kry策略
  8. SpringCloud Hystrix的超时时间配置以及监控平台搭建
  9. spring随笔(二) AOP
  10. 安大计算机学院院长汤进,淮北师范大学