目录
1.1 CountDownLatch的内部类Sync源码分析
1.4 await(long timeout,TimeUnit unit)源码分析
2.1 await() / await(Long timeout,TimeUnit unit)
2.2 这两个方法最后都会调用doawait()方法;这也是CyclicBarrier的核心方法
2.5 isBroken()方法 判断栅栏是否处在中断的状态
1.CountDownLatch
锁和信号量是控制并发,但是CountDownLatch(倒数计数器)是提供并发(让线程一起等待到某个条件一起触发执行),个人感觉CountDownLatch在平常工作环境并不常用,最常见的场景就是开启多个线程同时执行某个任务,等所有任务执行完成后再做结果汇总;在countdownLatch出现之前都是使用join()方法来实现,但是join不够灵活,不能够满足不同场景下的需求·
1.1 CountDownLatch的内部类Sync源码分析
//Sync继承 AQS使用起state属性来作为count
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造器
Sync(int var1) {
//设置state的值为count
this.setState(var1);
}
//获取count的值,及state的值
int getCount() {
return this.getState();
}
//重写父类tryAcquireShared的方法
protected int tryAcquireShared(int var1) {、
//如果state值为0;返回1否则返回-1
return this.getState() == 0 ? 1 : -1;
}
//重写 父类tryReleaseShared方法
protected boolean tryReleaseShared(int var1) {
for (;;) {
//获取state的值
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//cas更新成功
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
1.2CountDownLatch 构造函数
public CountDownLatch(int count) {
//构造函数传入的值必须大于0;否则抛出异常
if (count < 0) throw new IllegalArgumentException("count < 0");
//创建一个Sync对象
this.sync = new Sync(count);
}
1.3await() 方法解析
当前线程调用了await()方法后会,会将当前线程阻塞直到出现下面两种情况之一才会返回:
当所有线程调用都调用了countDown方法后,也就是说调用了await方法的都要在调用countDown方法一遍使计数器的值为0
其他线程调用了当前线程的interrupt方法中断了当前线程, 当前线程会抛出interruptedException异常返回
public void await() throws InterruptedException {
//sync调用父类的acquireSharedInterruptibly方法
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被中断,则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//如果tryAcquireShared小于0,则进入AQS同步队列
if (tryAcquireShared(arg) < 0)
//调用AQS的方法进入同步队列
doAcquireSharedInterruptibly(arg);
}
Sync类的tryAcquireShared方法在state等于0时返回1;否则返回-1
回到AQS的AcquireSharedInterruptibly方法,当Sync类的tryAcquireShared返回1则回到AQS的AcquireSharedInterruptibly方法返回,即await方法返回;
1.4 await(long timeout,TimeUnit unit)源码分析
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
AQS中的tryAcquireSharedNanos方法
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
AQS中的doAcquireSharedNanos方法
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
当线程调用了countDownLacth方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回
当前线程都调用了countDownLacth对象的countDown方法,也就是计数器为0的时候,这个时候返回true
设置timeout时间到了,因为超时儿返回false
其他线程调用了当前线程的interrupt方法中断了当前线程,当前线程会抛出InterruptException异常后返回
2. countDown()方法
当前线程调用了该方法后悔递减计数器的值,递减后如果计数器为0,则会唤醒await方法而被阻塞的线程,否则什么都不做
public void countDown() {
sync.releaseShared(1);
}
2.1 AQS中的releaseShared方法
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared方法需要子类实现;具体实现在CountDownLatch内部类Sync中的tryReleaseShared方法
2.3 AQS中的doReleaseShared 方法:
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
2. CyclicBarrier
栅栏是强制要求线程到达某个临界点才能执行,当都到达时,一起跳过Cyclicbarrier对象执行,从字面意思理解可以看出循环执行,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。循环是因为当等待线程都被释放后,CyclicBarrier对象可以被重用
1.1Cyclicbarrier类的属性
/** 对象入口的重如锁 */
private final ReentrantLock lock = new ReentrantLock();
/**用于线程间等待与唤醒操作 */
private final Condition trip = lock.newCondition();
/** 拦截的线程数 */
private final int parties;
/* 所有线程都到达barrier时执行的任务 */
private final Runnable barrierCommand;
/** 当前Generation ,每当屏障失效或者开闸之后都会自动替换掉从而实现重置的功能 */
private Generation generation = new Generation();
/**
* 剩余阻塞线程数
*/
private int count;
1.2 CyclicBarrier的内部类
//每次使用CyclicBarrier对象都会关联一个Generation 对象
//当CyclicBarrier对象发生trip或者reset时,对应的Generation 会发生改变
private static class Generation {
//标示当前的CyclicBarrier对象是否已经处在中断的状态
boolean broken = false;
}
1.3 Cyclicbarrier的构造器
//创建拦截指定线程数的CyclicBarrier对象
//并且可以指定在所有线程都进入栅栏后的执行动作
public CyclicBarrier(int parties, Runnable barrierAction) {
//校验parties值的合法性
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
/**
* 创建拦截指定线程数的CyclicBarrier对象
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
CyclicBarrier,默认构造方法是CyclicBarrier(int parties),参数代表屏障拦截的线程数量,每个线程使用await方法告诉CyclicBarrier我已经到达了屏障,然后线程被阻塞在这;另一个构造函数,用于线程到达屏障时优先执行barrierAction操作,方便处理更复杂的场景
2.1 await() / await(Long timeout,TimeUnit unit)
//无参,不带超时时间参数的方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
//有超时时间的方法
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
2.2 这两个方法最后都会调用doawait()方法;这也是CyclicBarrier的核心方法
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//重入锁,获取后控制并发以串行
lock.lock();
try {
//获取当前代
final Generation g = generation;
//如果当前代坏了,则抛出BrokenBarrierException异常
if (g.broken)
throw new BrokenBarrierException();
//判断当前线程是否被中断
if (Thread.interrupted()) {
//设置当前代被损坏状态为true,并通知其他阻塞在次栅栏的线程
breakBarrier();
throw new InterruptedException();
}
// count 值自减,赋值给index
int index = --count;
//如果index值为0;说明这是最后一个到栅栏的线程
if (index == 0) { // tripped
//任务是否被执行的标志
boolean ranAction = false;
try {
//获取所有线程到达栅栏后要执行的任务
final Runnable command = barrierCommand;
//如果command部位空,command执行run方法
if (command != null)
command.run();
ranAction = true;
//更新栅栏的状态,并唤醒所有阻塞在这个栅栏的线程
nextGeneration();
return 0;
} finally {
//如果执行栅栏时失败了
if (!ranAction)
//设置当前代的broken状态为true,唤醒所有线程
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) {
//捕获InterruptedException 异常
//如果当前代没有被损坏
if (g == generation && ! g.broken) {
//设置broken状态为true
//并通知阻塞在次栅栏的其他线程
breakBarrier();
//把捕获的异常继续抛出
throw ie;
} else {
// 上面条件不满足,说线程不是这代的
//不会影响当前这代栅栏的执行只会,标记中断
Thread.currentThread().interrupt();
}
}
//如果当前代被破坏
if (g.broken)
throw new BrokenBarrierException();
//g != generation表示正常换代了
//返回当前线程所在栅栏的下标
//如果g == generation说明还没有换代,那为什么会醒呢?
//因为一个线程可以使用多个栅栏
//当别的栅栏唤醒这个线程,就会走到这里,所以需要判断是否是当前代
//正是因为这个原因才需要generation来保证正确
if (g != generation)
return index;
//如果有时间限制,且小于等于0;
if (timed && nanos <= 0L) {
//唤醒所有线程 # 并抛出异常
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
等待状态:
如果该线程不是最后一个调用await方法的线程,则它会一直处在等待状态,除非发生以下情况之一
- 最后一个线程到达,即index=0;
- 某个参与线程等待超时;
- 某个参与线程被中断
- 调用了Cyclicbarrierde reset方法,将屏障重置为初始状态
BrokenBarrierException 异常
如果一个线程处在等待状态时,如果其他线程调用reset()或者调用的barrier原本就是被破坏的,则抛出BrokenBarrierException异常
任何线程在等待时被中断了,则其他线程都将抛出BrokenBarrierException异常,并将barrier置于损坏状态
2.2breakBarrier方法
默认barrier是没有损坏的
当barrier损坏了或者有一个线程中断了,则通过breakBarrier来终止所有线程
当barrier损坏或者有一个线程中断了,则通过breakBarrier来终止所有线程
在breakBarrier中除了将broken设置为true,还会调用signall将在CyclicBarrier处在等待状态的线程全部唤醒
private void breakBarrier() {
//设置状态
generation.broken = true;
//恢复正在等待进入屏障的线程数量
count = parties;
//唤醒所有线程
trip.signalAll();
}
2.3nextGeneration() 方法
//当barrier发送trip时,用于更新状态并唤醒每一个线程
//这一个方法只有持有lock时被调用
private void nextGeneration() {
// signal completion of last generation
//唤醒所有线程
trip.signalAll();
//恢复正在等待的进入屏障的线程数量
count = parties;
//新生一代
generation = new Generation();
}
2.4 reset()方法
/**
*将barrier状态重置,如果此时有线程在barrier处等待,他们会抛出BrokenBarrierException并返回
*注意:由于其他原因发生broken后重置可能会很复杂,线程需要一些方式来完成同步,并选择一种方式来完成reset
*相对为后续的使用重建一个barrier,次重置操作更受欢迎
*注意:这是一个需要加锁的操作
*/
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
2.5 isBroken()方法 判断栅栏是否处在中断的状态
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}