Semaphore,翻译叫做信号灯,是用来做资源访问限制的,他维持了一个准许指令的集合,如果当前没有可以指令的话,调用一次acquire就会将当前的线程阻塞,没调用一次release就会将当前线程持有的指令还回指令集合。他的内部实现跟CountDownLatch类似,也是直接使用的aqs,在创建的时候就会设定一个state标记,用来表示可以同时被使用准许指令的最大值,在某个线程调用acquire的时候先判断这个值大于0,如果是的话表示当前可以获得一个准许,否则说明当前的指令已经全部用完,当前的线程被挂起,进入aqs的队列中。当某个已经获得指令的线程调用release的时候要把获取的指令返还,也就是将state标记增大。大体了解了意思之后,还是看看他的源码吧:
1、构造方法:
public Semaphore(int permits, boolean fair) {//第一个参数表示可以同时被获取的指令的个数,第二个参数表示在获取指令的时候是否是公平的,这个和ReentrantLock的公平的参数是一样的,如果是公平的,则任何获取操作都会先判断当前的aqs的队列中是否有等待的线程,如果有则排在最后面,如果是不公平的,则直接获取 sync = fair ? new FairSync(permits) : new NonfairSync(permits);//无论是fair还是不fair的都是aqs的子类,permits最终会被设置为state标记的大小。这里我以非公平的aqs为例。 }
2、获取锁的方法,这个是会抛异常的,即如果线程被挂起,其他线程调用了这个线程的interrupt方法,就会抛InterruptedException异常。
public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1);//调用aqs的获取方法,这个方法都是直接调用的aqs的方法,先判断是否可以获取,即tryAcquireShared方法,如果返回正数表示可以获取锁,否则排队等待。我们看一下非公平的aqs的代码 }
final int nonfairTryAcquireShared(int acquires) {//要获取的指令的数量 for (;;) { int available = getState();//当前的标记 int remaining = available - acquires;//最终返回的就是这个值,如果要获取的值小于state,则表示获得指令,否则没有获得指令。 if (remaining < 0 || compareAndSetState(available, remaining))//如果小于0的话则直接返回,表示获取失败,第二个compareXXX是cas更新state的值。 return remaining; } }
经过前面的博客的介绍,尤其是ReentrantLock和CountDownLatch,如果返回的是小于0的,就会加入到队列中,这里就不再重复贴代码了。返回的正负值的判断是根据当前的state的值和acquire的大小判断的。
3、获取所得方法,这个不会抛异常,
public void acquireUninterruptibly() { sync.acquireShared(1);//不抛异常的阻塞 }
4、一次获取多个指令的方法:
public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits);//他的思路和上面还是一样的,只不过将state减小的值更大一些,不再是1. }
acquire的方法基本原理都是一样的,在获取不到的时候就会排队,这个类没有限定时间的排队等待。
5、尝试性的获取指令,有好多个,都是tryAcquire开头的,有带有超时时间的,有的带有要获取的指令的个数,他的思路和ReentrantLock中的aqs是一样的,这里只写一个
public boolean tryAcquire() { return sync.nonfairTryAcquireShared(1) >= 0;//判断当前的state标记的值是否大于1,如果是则获得指令并返回true,否则不获取指令也不排队,直接返回false。nonfairTryAcquireShared方法在上面介绍过了, }
6、释放锁的方法;release方法,有的是释放一个,有的是多个指令。最终调用的是aqs的如下方法:
protected final boolean tryReleaseShared(int releases) { for (;;) { int current = getState();//当前的state int next = current + releases;// if (next < current) // overflow throw new Error("Maximum permit count exceeded"); if (compareAndSetState(current, next))//cas设置state,这个方法只会返回true,不会返回false。 return true; } } 从这个方法来看,是可以release的指令的数量多余获取的指令的数量,在方法里面并没有判断的地方。
在成功的返回true只会,这个方法还会调用doReleaseShared方法,很容易就能明白,他是为了唤醒因为获取不到指令而park的线程,这个队列是shared,即一下唤醒所有的阻塞的线程,但是由于state的大小的限制,很多的线程由于没有获取指令又一次被park了,这一点和CountDownLatch的思路一样。
7、获得当前剩余的指令的数量:
public int availablePermits() { return sync.getPermits();//里面就是调用的获取state的方法,查看state的值。 }
8、将当前的指令全部取消:
public int drainPermits() { return sync.drainPermits();//drain有使...干涸的意思,即将当前剩余的所有的指令全部取消。 }
sync的drainPermits方法:
final int drainPermits() { for (;;) { int current = getState();//当前的state if (current == 0 || compareAndSetState(current, 0))//如果当前的state是0,直接返回,如果不是0,则通过cas设置为0,这样就没有可以获取的指令的了 return current; } }
通过这个方法以及上面的release方法,可以发现Semaphore并没有严格的限制获取的指令必须归还,比如release中就可以归还大量的指令,也可以通过上面的drainPermtis将所有的指令全部取消,也就是这个指令的数量是可以动态改变的。
就这么简单,又学了一个juc的常用类。
相关推荐
本项目"tuling-juc-final.zip"显然聚焦于Java并发编程的实践,通过一系列代码示例来演示和解释Java内存模型(JMM)、`synchronized`关键字以及`volatile`关键字的使用。下面我们将深入探讨这些核心概念。 Java内存...
java - juc - 多线程 - 学习 -思维导图
Java-JUC-多线程进阶 Java-JUC-多线程进阶resources是 Java 并发编程的高级课程,涵盖了 Java 中的并发编程概念、线程安全、锁机制、集合类、线程池、函数式接口、Stream流式计算等多个方面。 什么是JUC JUC...
8. **线程通信**:`Semaphore`(信号量)、`CountDownLatch`(倒计时器)、`CyclicBarrier`(循环栅栏)和`Phaser`(屏障)等工具类,用于控制线程间的同步和通信,实现特定的并发模式。 9. **Fork/Join框架**:...
- **CountDownLatch/CyclicBarrier/Semaphore**:信号量类,用于控制并发线程数量或同步点。 8. **FutureTask** - **FutureTask**:表示一个异步计算的结果,它可以被取消,查询是否完成,获取或检查结果。 9. *...
"Java 多线程与并发(7-26)-JUC - 类汇总和学习指南" Java 多线程与并发是 Java 编程语言中的一部分,用于处理多线程和并发编程。Java 提供了一个名为 JUC(Java Utilities for Concurrency)的框架,用于帮助开发者...
JUC的同步器包括CountDownLatch、CyclicBarrier、Semaphore等,它们是为了解决多个线程协作完成特定任务的工具。例如,CountDownLatch可以让一些线程等待其他线程完成操作后再继续执行,而CyclicBarrier则让一组线程...
学习狂神说的juc编程的笔记
juc-jenkins-2018 JUC Jenkins 2018演示源代码 先决条件 为了运行此演示,必须有一个有效的JDK,git命令以及curl。 克隆存储库 将此存储库克隆到您家中的某个位置: git clone ...
【JUC 概念】 Java Util Concurrency (JUC) 是 Java SDK 中的一个核心包,位于 `java.util.concurrent` 下,它提供了丰富的线程同步和并发工具类,旨在简化多线程编程,提高程序的并发性能。JUC 包含了线程池、并发...
在Java中,`java.util.concurrent`包包含了大量并发控制和并行计算的类与接口,如ExecutorService、Semaphore、CountDownLatch等,它们使得开发者能够更容易地编写出高性能、多线程的程序。 NIO(New Input/Output...
微程序控制器实验1. 连接好实验线路,检查无误后接通电源。2. 将编程开关(MJ20)置为PROM(编程)状态。3. 将STATE UNIT中的STEP置为“STEP”状态,STOP置为“RUN”状态。4. 在UA5-UA0开关上置要写的某个微地址(八进制)...
本项目"juc-learn"专注于JUC相关源码的分析和使用介绍,旨在帮助开发者深入理解并熟练运用这些并发工具。 1. **并发基础** 在Java中,多线程是并发编程的基础。通过创建Thread对象或实现Runnable接口,我们可以...
Java 并发编程专题(六)----浅析(JUC)Semaphore Java 并发编程专题(六)----浅析(JUC)Semaphore 是 Java 并发编程中的一种重要机制,它主要用于控制多个线程访问共享资源的同时性。Semaphore 是一种信号量...
《JUC:Java并发编程的艺术》 在Java世界中,JUC(Java Util Concurrency)是并发编程的核心库,它提供了丰富的...通过深入学习JUC-master项目,我们可以更深入地理解Java并发编程的原理和实践,提升我们的编程技能。
juc-demo JUC包下常用工具练习Demo 内容: 1、Semaphore 2、CountDownLatch 3、CyclicBarrier 4、ReentrantLock + Condition实现阻塞队列 Created by @minghui.y.
1、Java并发体系-第一阶段-多线程基础知识 2、Java并发体系-第二阶段-锁与同步-[1] 3、Java并发体系-第二阶段-锁与同步-[2] 4、Java并发体系-第二阶段-锁与同步-[3] ...7、Java并发体系-第四阶段-AQS源码解读-[1]
JUC线程高级,
《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...
教程视频:在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类, 用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文...