深入理解J.U.C并发工具类
1. CountDownLatch
1.1 什么是CountDownLatch
如果要用一句话说明CountDownLatch的用处,那就是用来控制一个线程等待多个线程
CountDownLatch是一个同步类工具,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成 CountDownLatch用一个给定的count初始化,
await方法会一直阻塞直到调用countDown方法使当前的count值变成0,之后所有等待线程被释放,并且任何后续的await调用立即返回
1.2 CountDownLatch典型应用
第一种用法:
假设有一场马拉松比赛,那么对参赛者的排名肯定是在所有参赛者跑完比赛之后进行,即N个线程执行操作,主线程等到N个子线程执行完毕之后再执行
package juc;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;
/**
* @author zhaobin11@baidu.com
*/
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
int threadCount = 10;
final CountDownLatch latch = new CountDownLatch(threadCount);
IntStream.range(0, threadCount).forEach(i ->
new Thread(() -> {
System.out.println(Thread.currentThread().getId() + "号选手开始出发");
try {
// 模拟运动员跑步耗时
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + "号选手已到达终点");
latch.countDown();
}).start()
);
latch.await();
System.out.println("所有选手到达终点,开始计算排名");
}
}
运行结果为
13号选手开始出发
16号选手开始出发
15号选手开始出发
12号选手开始出发
14号选手开始出发
19号选手开始出发
18号选手开始出发
17号选手开始出发
20号选手开始出发
21号选手开始出发
19号选手已到达终点
20号选手已到达终点
17号选手已到达终点
21号选手已到达终点
16号选手已到达终点
13号选手已到达终点
15号选手已到达终点
18号选手已到达终点
14号选手已到达终点
12号选手已到达终点
所有选手到达终点,开始计算排名
CountDownLatch允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行
CountDownLatch有3个重要的变量&方法
- count:初始化count数表示子线程计数器,只有为0时,主线程才会向下执行
- countDown()方法:计数器的值-1,如果计数达到0,释放所有等待的线程
- await()方法:使主线程等到计数器为0才执行
接下来的两种用法是作者Doug Lea给出来的关于CountDownLatch比较好的一些实践,更具模板性
第二种用法:
CountDownLatch初始化为N可以用来作一个线程等待,直到N个线程完成某项操作,或某些动作已经完成N次
class Driver { // ...
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
}
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
} // return;
}
void doWork() { ...}
}
第三种用法:
另一个典型的用法是把一个问题划分为N个部分,每一个部分用一个线程执行并把所有的线程放在Executor里面排队,执行完成就调用countDown方法,当所有子线程都执行完成,接应线程就可以通过await,即不被CountDownLatch的await方法阻塞了
class Driver { // ...
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ...
for (int i = 0; i < N; ++i) // create and start threads
e.execute(new WorkerRunnable(doneSignal, i));
doneSignal.await(); // wait for all to finish
}
}
class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
try {
doWork(i);
doneSignal.countDown();
} catch (InterruptedException ex) {
} // return;
}
void doWork() { ...}
}
1.3 CountDownLatch源码分析
// CountDownLatch底层由AQS支持
public class CountDownLatch {
// 内部类Sync
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (; ; ) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
// 构造一个给定初始化为count的CountDownLatch count是线程能通过await方法之前需要调用countDown方法的次数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/*
使当前线程在倒计数为零前一直等待,除非线程被中断
如果当前count值为0,那么await方法会立即返回 如果当前count值大于0,当前线程会处于休眠状态直到:
1.由于调用countDown方法使得count值达到0 2.其他某个线程中断了当前线程
如果当前线程在进入await方法时已经设置了中断状态,或者在等待时被中断,则抛出InterruptedException异常,并清除当前线程的已中断状态
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/*
使当前线程在倒计数为零前一直等待,除非线程被中断或超出了指定的等待时间
如果当前count值为0,那么await方法会立即返回true 如果当前count值大于0,当前线程会处于休眠状态直到:
1.由于调用countDown方法使得count值达到0 2.其他某个线程中断了当前线程 3.已超出指定的等待时间
如果计数达到零,那么await(long timeout, TimeUnit unit)会返回true
如果当前线程在进入await方法时已经设置了中断状态,或者在等待时被中断,则抛出InterruptedException异常,并清除当前线程的已中断状态
如果超出了指定的等待时间,则返回值为false,如果指定时间小于或等于0,那么await(long timeout, TimeUnit unit)根本不会等待,即不会阻塞主线程
timeout:要等待的最长时间 unit:timeout参数的时间单位
如果计数到达零,则返回true 如果在计数到达零之前超过了等待时间,则返回false
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/*
递减闭锁的count值,如果计数到达零,则释放所有等待的线程
当前count值大于0,递减 当前count等于0,被阻塞的线程可以被调度了,且count值不会递减了
*/
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
2. CyclicBarrier
2.1 什么是CyclicBarrier
如果要用一句话说明CyclicBarrier的用处,那就是用来控制多个线程互相等待
CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点,之所以称为cyclic的barrier,是因为等待线程释放后栅栏可以重用
对于失败的同步尝试,CyclicBarrier用了一种all-or-none破坏模式:
如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在屏障点等待的其他所有线程也会通过BrokenBarrierException异常离开屏障点,
如果几乎同时被中断,那么会通过InterruptedException以反常的方式离开
CyclicBarrier实现思路:就是设置一个计数,每当有线程达到时,计数count-1,Condition.await进入阻塞,当count=0,那么可以signalAll,让所有线程得以唤醒,唤醒后立即重置
2.2 CyclicBarrier典型应用
package juc;
import java.util.concurrent.CyclicBarrier;
import java.util.stream.IntStream;
/**
* @author zhaobin11@baidu.com
*/
public class CyclicBarrierTest1 {
// 如果await次数小于parties则会永远等待
private static final int parties = 2;
private static CyclicBarrier c = new CyclicBarrier(parties);
// 0 1 或者 1 0
public static void main(String[] args) {
IntStream.range(0, parties).forEach(i ->
new Thread(() -> {
try {
c.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.print(i + " ");
}).start()
);
}
}
CyclicBarrier中文叫循环栅栏,可以让一组线程到达一个屏障(也叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续执行
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await()方法告诉CyclicBarrier自己已经到达了屏障,然后当前线程被阻塞
CyclicBarrier还提供了一个更高级的构造函数CyclicBarrier(int parties,Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction
package juc;
import java.util.concurrent.CyclicBarrier;
import java.util.stream.IntStream;
/**
* @author zhaobin11@baidu.com
*/
public class CyclicBarrierTest2 {
private static final int parties = 2;
private static CyclicBarrier c = new CyclicBarrier(parties, () -> System.out.print(3 + " "));
// 3 1 0 或者 3 0 1
public static void main(String[] args) {
IntStream.range(0, parties).forEach(i ->
new Thread(() -> {
try {
c.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.print(i + " ");
}).start()
);
}
}
CyclicBarrier可以被中断
package juc;
import java.util.concurrent.CyclicBarrier;
import java.util.stream.IntStream;
/**
* @author zhaobin11@baidu.com
*/
public class CyclicBarrierTest3 {
private static final int parties = 2;
private static CyclicBarrier c = new CyclicBarrier(parties);
// true true
public static void main(String[] args) {
IntStream.range(0, parties).forEach(i -> {
Thread thread = new Thread(() -> {
try {
c.await();
} catch (Exception e) {
System.out.print(c.isBroken() + " ");
}
});
thread.start();
thread.interrupt();
});
}
}
2.3 CyclicBarrier和CountDownLatch的区别
CyclicBarrier | CountDownLatch |
---|---|
减计数方式 | 加计数方式 |
计数为0时释放所有等待的线程 | 计数达到指定值时释放所有等待的线程 |
计数达到指定值时释放所有等待的线程 | 计数达到指定值时,计数置为0重新开始(可重复) |
调用countDown()方法计数减1,调用await()方法只进行阻塞,对计数没任何影响 | 调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞 |
2.4 CyclicBarrier源码分析
public class CyclicBarrier {
/*
屏障的每次use都意味着一次generation instance。当屏障重置时,generation会发生改变,在用屏障的过程中,可能有许多代与线程相关联,
这是因为锁分配给等待线程的方式的不确定性,但是在同一个时间只能有一个generation处于活动状态,而其他所有的都会被破坏
*/
private static class Generation {
boolean broken = false;
}
// 守护屏障入口的锁
private final ReentrantLock lock = new ReentrantLock();
// Condition即条件谓词
private final Condition trip = lock.newCondition();
// 屏障释放前需要等待的线程数量
private final int parties;
// 屏障突破后,要执行的命令
private final Runnable barrierCommand;
// 轮,相当于一次集合到释放为一轮,一轮一轮的进行
private Generation generation = new Generation();
// 重置后count=parties,count表示还需要等待的线程数量才能结束当前轮进入下一轮
private int count;
// 更新屏障的状态并唤醒所有等待的线程,只有持有锁时才会被调用
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
// 设置当前屏障的generation并唤醒所有等待的线程,只有持有锁时才会被调用
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
// timed:是否有时间限制 nanos:wait的纳秒数
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (; ; ) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && !g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
// 创建一个参数为parties带有barrierAction的CyclicBarrier
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
// 创建一个参数为parties的CyclicBarrier
public CyclicBarrier(int parties) {
this(parties, null);
}
// 返回barrier拦截的线程数量
public int getParties() {
return parties;
}
/*
在所有的参与者线程调用await方法之前,屏障一直阻塞已经await的线程,如果当前线程不是最后一个线程,当前线程会处于休眠状态直到:
1.最后一个线程到达
2.其他某个线程中断当前线程
3.其他某个线程中断另一个等待线程
4.其他某个线程在等待barrier时超时
5.其他某个线程在barrier上调用reset
如果当前线程在进入await方法时已经设置了中断状态,或者在等待时被中断,则抛出InterruptedException异常,并清除当前线程的已中断状态
如果在线程处于等待状态时barrier被reset(),或者在调用await时barrier被损坏,或任意一个线程正处于等待状态,则抛出BrokenBarrierException异常
如果任何线程在等待时被中断,则其他所有等待线程都会抛出BrokenBarrierException异常,且barrier会被置为损坏状态
如果当前线程是最后一个到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程
会运行barrier action
如果在执行屏障操作过程中发生异常,则异常会传播到当前线程中,且barrier会被置为损坏状态
await方法返回的是到达的当前线程的索引,其中getParties()-1表示到达的第一个线程,0表示到达的最后一个线程
如果当前线程在等待时被中断则抛出InterruptedException
如果另一个线程在当前线程等待时被中断或超时,或者重置了barrier,或者在调用await时barrier被损坏,或由于异常而导致屏障操作(如果存在)失败则抛出BrokenBarrierException
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
// 带有限制时间的await方法
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
// 查询屏障是否处于损坏状态
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
// 重置屏障为初始状态
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
// 返回当前在屏障处等待的线程数量
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
}
3. Semaphore
3.1 什么是Semaphore
Semaphore就是操作系统中的信号量,可以控制对互斥资源的访问线程数
Semaphore是一个计数信号量,从概念上讲,信号量维护了一个许可集,在许可可用前会阻塞每一个acquire,直到许可可用才能获取
release会添加一个许可,从而可能释放一个正被阻塞的acquire
Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目
semaphore初始化为1,那么最多只有一个可用的许可,可用作一个相互排斥的锁,也称为二进制信号量
Semaphore的构造方法可选地接受一个公平参数,当设置为false时,不对线程获取许可的顺序作任何保证,即插队是允许的,也就是说可以在等待的线程前为调用acquire的线程分配一个许可,当设置为true时,Semaphore保证对于任何调用acquire的线程而言,都按照调用方法的顺序即FIFO来选择线程获得许可所以可能某个线程在另一个线程之前调用了acquire,却在之后到达排序点,并且从方法返回时也类似
tryAcquire方法默认是非公平的,一般对于控制资源访问的信号量都初始化为公平的,以确保所有线程都可访问资源,非公平的方式会比公平的方式在吞吐量上更有优势
3.2 Semaphore典型应用
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author zhaobin11@baidu.com
*/
public class SemaphoreTest1 {
private final static int threadCount = 20;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(); //获取一个许可
fun(threadNum);
semaphore.release(); //释放一个许可
// 对应结果为0 1 2-3 4 5-6 8 7-10 11 9-12 14 13-15 16 17-18 19
} catch (Exception e) {
e.printStackTrace();
}
});
}
exec.shutdown();
}
private static void fun(int threadNum) throws Exception {
System.out.print(threadNum + " ");
Thread.sleep(1000);
}
}
Semaphore还提供acquire及release方法一次获取或释放多个许可,但是要注意的是,如果设置为非公平的方式,这可能会增加不确定延期的风险(同时获取多个许可可能会产生竞争)
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author zhaobin11@baidu.com
*/
public class SemaphoreTest2 {
private final static int threadCount = 20;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(3); //获取多个许可
fun(threadNum);
semaphore.release(3); //释放多个许可
// 对应结果为0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19
} catch (Exception e) {
e.printStackTrace();
}
});
}
exec.shutdown();
}
private static void fun(int threadNum) throws Exception {
System.out.print(threadNum + " ");
Thread.sleep(1000);
}
}
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author zhaobin11@baidu.com
*/
public class SemaphoreTest3 {
private final static int threadCount = 20;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
if (semaphore.tryAcquire()) { //尝试获取一个许可
fun(threadNum);
semaphore.release(); //释放一个许可
}
// 对应结果为0 2 1
} catch (Exception e) {
e.printStackTrace();
}
});
}
exec.shutdown();
}
private static void fun(int threadNum) throws Exception {
System.out.print(threadNum + " ");
Thread.sleep(1000);
}
}
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author zhaobin11@baidu.com
*/
public class SemaphoreTest4 {
private final static int threadCount = 20;
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
if (semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)) { //尝试获取一个许可
fun(threadNum);
semaphore.release(); //释放一个许可
}
// 对应结果为1 3 0-2 4 6-7 5 8-9 11 12-10 13 15-16
} catch (Exception e) {
e.printStackTrace();
}
});
}
exec.shutdown();
}
private static void fun(int threadNum) throws Exception {
System.out.print(threadNum + " ");
Thread.sleep(1000);
}
}
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();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
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;
} else
return false;
}
}
return false;
}
}
3.3 Semaphore源码分析
// Semaphore也是通过AQS实现的
public class Semaphore implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (; ; ) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (; ; ) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
final void reducePermits(int reductions) {
for (; ; ) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {
for (; ; ) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
// 非公平方式
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);
}
}
// 公平方式
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())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
// 创建具有给定的许可数和非公平的公平设置的Semaphore,注意给定的permits可能为负数,在这种情况下释放许可必须保证在获取许可之前
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
// true表示Semaphore保证按照FIFO的顺序对线程授予许可
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
/*
从semaphore中获取一个许可,线程会一直被阻塞直到获取一个许可或是被中断
获取一个许可后立即返回,并把许可数减1,如果没有可用的许可,当前线程会处于休眠状态直到:
1.某些其他线程调用release方法,并且当前线程是下一个要被分配许可的线程
2.某些其他线程中断当前线程
如果当前线程被acquire方法使得中断状态设置为on或者在等待许可时被中断则抛出InterruptedException,并且清除当前线程的已中断状态
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/*
从semaphore中获取一个许可,线程会一直被阻塞直到获取一个许可或是被中断
获取一个许可后立即返回,并把许可数减1
如果没有可用的许可,当前线程会处于休眠状态直到:某些其他线程调用release方法,并且当前线程是下一个要被分配许可的线程
如果当前线程在等待许可时被中断,那么它会接着等待,但是与没有发生中断相比,为线程分配许可的时间可能改变
*/
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
/*
仅在有可用的许可且在调用的时间内,那么会从semaphore中获取一个许可
获取一个许可并立即返回,返回值为true,当前的许可数-1,如果没有可用的许可,方法会立即返回false
即使semaphore是基于公平方式实现的,调用tryAcquire方法也会无视公平性,只要有许可,就会获得而不管是否有线程在排队等待
*/
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
/*
从semaphore中获取一个许可,仅在给定的时间内且当前线程未被中断
获得一个许可并立即返回,返回值为true,当前许可数-1
如果没有可用的许可,当前线程会处于休眠状态直到:
1.某些其他线程调用release方法,并且当前线程是下一个要被分配许可的线程
2.某些其他线程中断当前线程
3.超出等待时间
如果当前线程被acquire方法使得中断状态设置为on或者在等待许可时被中断则抛出InterruptedException,并且清除当前线程的已中断状态
如果超出给定等待时间会返回false,如果给定时间小于等于0,那么方法根本不会等待
timeout:等待获取许可的时间限制 unit:timeout的时间参数
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/*
释放一个许可,并把它放回semaphore中,可用许可数+1
如果任意线程试图获取许可,则选中一个线程并把释放的许可给它,不要求释放许可的线程必须通过调用acquire方法来获取许可
*/
public void release() {
sync.releaseShared(1);
}
/*
从semaphore中获取给定数量的许可,一直阻塞直到全部获取或者线程被中断
获取给定数量的许可并立即返回,许可数-给定数量
如果没有足够的可用许可,那么线程会处于休眠状态直到:
1.其他某些线程调用release释放许可,且当前线程是下一个被分配允许的线程并且可用许可的数目满足给定数量
2.某些其他线程中断当前线程
如果当前线程被acquire方法使得中断状态设置为on或者在等待许可时被中断则抛出InterruptedException,并且清除当前线程的已中断状态
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
// 从semaphore中获取给定数量的许可,一直阻塞直到全部获取
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
// 从semaphore中获取给定数量的许可,仅在调用时间内所有许可可被获取
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
// 仅在给定的时间内且当前线程未被中断,那么会从semaphore中获取给定数量的许可
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
// 释放给定数量的许可,并放回semaphore中,可用许可数+给定数量
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
// 返回semaphore中可用的许可数
public int availablePermits() {
return sync.getPermits();
}
// 获取并返回立即可用的所有许可
public int drainPermits() {
return sync.drainPermits();
}
// 根据指定的缩减量减小可用许可的数目,不同于acquire,在许可变为可用的过程中,它不会阻塞等待
protected void reducePermits(int reduction) {
if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
// 如果semaphore的公平设置为true,则返回true
public boolean isFair() {
return sync instanceof FairSync;
}
// 查询是否有线程正在等待获取,需要注意,因为可能同时发生取消,所以返回true并不保证有其他线程等待获取许可
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
// 返回正在等待获取的线程的估计数量,仅是估计的数量,因为线程的数量在遍历的过程中可能会动态地变化
public final int getQueueLength() {
return sync.getQueueLength();
}
// 返回一个collection,包含可能等待获取的线程
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
// 返回信号量的状态,即当前的许可数
public String toString() {
return super.toString() + "[Permits = " + sync.getPermits() + "]";
}
}