AQS常用组件源码分析
这里主要介绍三种组件:Semaphore,CountDownLatch,CyclicBarrier
Semaphore
Semaphore代表一个共享式的信号量,资源可以同时被多个线程使用
属性
// 实现了AbstractQueuedSynchronizer的并发控制器
// 可以使用公平的,也可以是不公平的,通过构造函数的参数可以控制
private final Sync sync;
构造函数
// 通过fair参数判断是公平的还是不公平的
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
下面主要介绍Sync和它的两个子类
Sync继承自AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
// 代表运行同时访问的线程个数
// 底层是设置到AQS的状态中
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
// Sync中实现的是不公平的获取资源
// 返回值如果大于等于0,代表资源获取成功,否则代表资源获取失败,如果资源获取失败,当前线程会进入等待队列中
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 获取当前剩余资源量
int available = getState();
// 计算获取资源后的剩余资源量
int remaining = available - acquires;
// 如果剩余资源量小于0,直接返回
// 如果剩余资源量大于零,使用cas更新剩余资源直到成功,或者在重复尝试更新资源剩余量的过程中,资源不量小于0,那么直接返回
// 从这里也可以看出,这里是不公平的,因为当前线程直接申请资源,并且没有理会等待队列中是否有先于当前线程申请资源的线程
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// 剩下的源码很简单就不分析了
// 都是使用cas操作来更新资源量
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);
}
// 使用的就是父类Sync的方法
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;
}
}
}
获取与释放
获取和释放资源非常简单,底层调用的都是Sync子类相应的方法
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
CountDownLatch
CountDownLatch的使用场景是这样的:一个线程需要等待一定数量的线程执行完毕之后,才能接着往下运行,否则将会阻塞,直到一定数量的线程执行完毕
其中多个线程在执行到某个位置时,会执行countdown来减小state
当state减小到0时,线程A被唤醒继续执行自己的任务
构造函数
传入的参数代表需要等待多少个线程
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
// CountDownLatch中只有非公平的
this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
// 只有state等于0时,才能申请到资源
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;
// 尝试减小state的值
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
获取和释放
public void await() throws InterruptedException {
// 底层会调用tryAcquiredShared方法
// 如果state不为0,那么将当前线程加入等待队列中
// 当state等于0时,会得到唤醒,从而继续执行线程的任务
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
// 底层会调用tryReleaseShare()减小state的值
sync.releaseShared(1);
}
CyclicBarrier
CyclicBarrier的使用场景是这样的:多个线程必须等待彼此到达某个执行位置时,才能一起往下执行,当且当指定的线程都到达了各自的目的位置时,可以另外执行一个任务,如果还有线程没有执行到各自的指定位置,那么就会阻塞等待其他任务执行到各自的指定位置
CyclicBarrier之所以叫Cyclic是因为,当所有线程都到代码的目的位置后,barrier释放,线程接着执行,会自动重新设置barrier,等待下一次所有线程执行各自的指定位置,即barrier可以循环使用
CyclicBarrier并没有使用AQS
属性
private static class Generation {
boolean broken = false;
}
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
构造函数
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 代表需要等待多少个线程执行到指定位置
this.parties = parties;
// 剩余未到达指定位置的线程个数
this.count = parties;
// 当所有线程都执行到各自的指定位置时,除了释放barrier,让线程往下执行,还可以另外启动一个线程,就是下面指定的barrier释放动作
this.barrierCommand = barrierAction;
}
获取和释放
当线程执行到指定位置需要等待其他线程时,会调用await()
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 一个generation代表一次完整的线程彼此等待过程
// 当线程都执行到各自的指定位置时,会调用generation的break(),来唤醒所有等待的线程,继续执行,
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
// 所有的线程都已经达到了等待位置
// 释放barrier
// 当最后一个线程执行到指定位置,会唤醒其他所有等待线程
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
// 重新创建一个generation,并且唤醒所有的等待线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 一直循环直到所有线程都执行到了特定位置,或者遇到中断或者等待超时
// 如果其他线程遇到中断也会导致barrier的broken,从而引起当前线程结束循环
// 等待其他线程执行到指定位置
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();
}
}
// 如果没有中断也没有等待超时,所有线程都是正常执行到了指定位置,就会执行nextGenration
// 在该方法中会唤醒所有等待线程,重新计数,重新建立一个新的generation
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
// 当因为中断或者等待超时而释放barrier时,会调用breakBarrier
// 此时并不会新建一个generation,只是会表示broken状态为true
// 同时也会唤醒所有线程,并且重新计数
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
总结一下流程:
(1)如果所有线程都正常的到达了指定的代码位置,就会运行barrier释放任务,并且重新设置generation以及count计数,所有等待线程接着往下执行,进入下一轮等待
(2)如果有线程等待超时或者被中断,标记generation为broken,唤醒所有等待线程并且抛出异常