目录
一、CountDownLatch
1、使用
2、实现
3、使用synchronized改造
二、CyclicBarrier
1、使用
2、定义
3、实现
4、使用synchronized改造
CountDownLatch表示一个计数器,子线程执行完毕会调用countDown方法将计数减1,主线程调用await,一直等待直到计数变成0或者等待超时了,用于主线程必须等待所有子线程执行完某个任务的情形;CyclicBarrier表示一个可重复使用的栅栏,所谓栅栏是指等待所有线程到达某个点后才开始执行某个任务,如果这个点在子线程执行任务前,则可确保所有的子线程同时开始执行任务,比如高并发测试场景,如果这个点是在子线程任务执行完成后,则可用于实现CountDownLatch的功能,本篇博客就讲解这两个类的具体用法和实现细节。
CountDownLatch不能让子线程停留在某个点上,只能通过countDown方法通知主线程子线程任务执行完成,且CountDownLatch的计数是可不重置的,即无法被重复使用,其测试用例如下:
@Test
public void test() throws Exception {
Random random = new Random();
int threadNum = 6;
CountDownLatch end=new CountDownLatch(threadNum);
Runnable test=new Runnable() {
@Override
public void run() {
try {
String name=Thread.currentThread().getName();
System.out.println(name + " 执行任务开始,时间:" + System.currentTimeMillis());;
Thread.sleep(random.nextInt(2000));
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
//只是将计数减1,无法让该线程在此处停留
end.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum; i++) {
new Thread(test).start();
}
end.await();
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
for (int i = 0; i < threadNum; i++) {
new Thread(test).start();
}
//无法重复使用,此时计数已经是0,await方法直接返回,不会阻塞
end.await();
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
}
其输出如下:
Thread-0 执行任务开始,时间:1585733772491
Thread-4 执行任务开始,时间:1585733772492
Thread-3 执行任务开始,时间:1585733772492
Thread-1 执行任务开始,时间:1585733772495
Thread-5 执行任务开始,时间:1585733772495
Thread-2 执行任务开始,时间:1585733772495
Thread-4 执行任务结束,时间:1585733773436
Thread-2 执行任务结束,时间:1585733773624
Thread-0 执行任务结束,时间:1585733773928
Thread-3 执行任务结束,时间:1585733774119
Thread-1 执行任务结束,时间:1585733774257
Thread-5 执行任务结束,时间:1585733774494
所有线程结束任务,时间: 1585733774494
Thread-6 执行任务开始,时间:1585733774494
Thread-7 执行任务开始,时间:1585733774494
Thread-8 执行任务开始,时间:1585733774495
Thread-10 执行任务开始,时间:1585733774495
//第二次调用await方法立即返回,子线程启动了但是还没有执行countDown方法
//就随着主线程的退出而退出了
所有线程结束任务,时间: 1585733774495
Thread-11 执行任务开始,时间:1585733774495
Thread-9 执行任务开始,时间:1585733774495
等待超时的情形测试用例如下:
@Test
public void test2() throws Exception {
Random random = new Random();
int threadNum = 6;
CountDownLatch end=new CountDownLatch(threadNum);
Runnable test=new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000+random.nextInt(1000));
String name=Thread.currentThread().getName();
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
end.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum; i++) {
new Thread(test).start();
}
//如果等待超时则被唤醒,继续执行下面的逻辑,主线程退出了,子线程也跟着退出了
end.await(1500, TimeUnit.MILLISECONDS);
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
}
其执行结果如下:
Thread-1 执行任务结束,时间:1585734211126
Thread-4 执行任务结束,时间:1585734211421
//主线程并没有等待所有的子线程执行完成就直接退出了
//其他子线程因为主线程退出了也退出了
所有线程结束任务,时间: 1585734211594
CountDownLatch的实现比较简单,核心就是一个继承自AbstractQueuedSynchronizer的内部类实现的,核心方法实现如下:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
//阻塞当前线程
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
//判断state是否为0,为0返回1,不为0返回-1
return (getState() == 0) ? 1 : -1;
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//创建一个新的节点并插入到链表中,注意如果链表为空会插入一个空的Node到链表中,然后再
//插入我们创建的Node
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//返回node的前一个节点,实际就是head节点
final Node p = node.predecessor();
if (p == head) {
//node是第一个插入的节点
int r = tryAcquireShared(arg);
if (r >= 0) {
//被唤醒后for循环进入此逻辑
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//head的初始状态为0,shouldParkAfterFailedAcquire将其改成SIGNAL然后返回false
//第二遍for循环就返回true,
//parkAndCheckInterrupt方法会park掉当前线程,如果被唤醒了返回是否被线程中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//如果tryAcquireShared小于0就会执行后面的doAcquireSharedNanos方法了
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
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 (;;) {
//node的前一个节点就是head节点
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;
//同上p的初始状态是0,第一遍shouldParkAfterFailedAcquire将其改成SIGNAL然后返回false,第二遍for循环就返回true
//执行后面的判断逻辑
//spinForTimeoutThreshold的值是1000,单位是纳秒,如果等待的时间小于该值则自旋
//否则让线程阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//唤醒等待的主线程
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//原子的修改state,如果nextc为0则返回true
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//head节点在doAcquireSharedInterruptibly方法中状态被改成了SIGNAL
if (ws == Node.SIGNAL) {
//如果设置失败就返回false,则继续下一次循环
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//唤醒head的下一个节点,即上面doAcquireSharedInterruptibly插入的一个节点
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
可以使用synchronized来代替上述内部类Sync,具体如下:
import java.util.concurrent.TimeUnit;
public class ObjectCountDownLatch {
private Object lock = new Object();
private int count;
public ObjectCountDownLatch(int count) {
if (count <= 0) throw new IllegalArgumentException();
this.count = count;
}
public void await() throws InterruptedException {
dowait(false, 0L);
}
public void await(long timeout, TimeUnit unit)
throws InterruptedException {
dowait(true, unit.toMillis(timeout));
}
private void dowait(boolean timed, long nanos)
throws InterruptedException {
synchronized (lock) {
if (count > 0) {
if (timed) {
lock.wait(nanos);
} else {
lock.wait();
}
}
}
}
public void countDown() {
synchronized (lock) {
count--;
if (count == 0) {
lock.notify();
}
}
}
}
测试用例如下:
@Test
public void test3() throws Exception {
Random random = new Random();
int threadNum = 6;
ObjectCountDownLatch end=new ObjectCountDownLatch(threadNum);
Runnable test=new Runnable() {
@Override
public void run() {
try {
String name=Thread.currentThread().getName();
Thread.sleep(random.nextInt(2000));
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
//只是将计数减1,无法让该线程在此处停留
end.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum; i++) {
new Thread(test).start();
}
System.out.println("main start await,时间: " + System.currentTimeMillis());
end.await();
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
}
@Test
public void test4() throws Exception {
Random random = new Random();
int threadNum = 6;
ObjectCountDownLatch end=new ObjectCountDownLatch(threadNum);
Runnable test=new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000+random.nextInt(1000));
String name=Thread.currentThread().getName();
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
end.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum; i++) {
new Thread(test).start();
}
System.out.println("main start await,时间: " + System.currentTimeMillis());
end.await(1500, TimeUnit.MILLISECONDS);
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
}
效果和CountDownLatch基本一致,其实现的思路和CountDownLatch也是一致的。
CyclicBarrier可以让所有子线程同时开始执行任务,也可以实现CountDownLatch的功能,即让主线程等待所有的子线程任务执行完成,测试用例如下:
class TaskThread extends Thread {
CyclicBarrier barrier;
private static Random random=new Random();
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(random.nextInt(1000));
System.out.println(getName() + " 到达栅栏A,时间:"+System.currentTimeMillis());
//await方法的返回值可用来确定各线程到达的顺序,0是最后一个到达的
//已到达的线程会阻塞等待最后一个线程到达
int num=barrier.await();
System.out.println(getName()+" 执行任务开始,时间:"+System.currentTimeMillis()+",num->"+num);
Thread.sleep(random.nextInt(2000));
System.out.println(getName()+" 执行任务结束,时间:"+System.currentTimeMillis());
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
public void test() throws Exception {
int threadNum = 6;
//回调函数是最后一个调用await,即最后一个到达栅栏的线程执行的
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务,时间:"+System.currentTimeMillis());
}
});
for(int i = 0; i < threadNum-1; i++) {
new TaskThread(barrier).start();
}
//await可以调用多次,一旦某个线程调用了await,要求其他的相关线程也要调用await,否则因为某个线程没有调用await,其他
//已调用await的线程就会被阻塞
//因此主线程中await的调用次数需要与子线程中的调用次数保持一致
int num=barrier.await();
//如果有一个或者多个线程因为中断或者等待超时了就返回true,否则返回false
System.out.println("isBroken->"+barrier.isBroken());
System.out.println("所有线程开始执行,时间: "+System.currentTimeMillis()+",num->"+num);
num=barrier.await();
System.out.println("所有线程结束任务,时间: "+System.currentTimeMillis()+",num->"+num);
}
上述用例的运行结果如下:
Thread-2 到达栅栏A,时间:1585708426763
Thread-3 到达栅栏A,时间:1585708426975
Thread-0 到达栅栏A,时间:1585708427303
Thread-1 到达栅栏A,时间:1585708427377
Thread-4 到达栅栏A,时间:1585708427378
#最后一个到达的,负责执行回调函数
Thread-4 完成最后任务,时间:1585708427378
#任务开始时间是基本一致的
Thread-4 执行任务开始,时间:1585708427381,num->0
isBroken->false
所有线程开始执行,时间: 1585708427382,num->5
Thread-2 执行任务开始,时间:1585708427382,num->4
Thread-3 执行任务开始,时间:1585708427382,num->3
Thread-0 执行任务开始,时间:1585708427382,num->2
Thread-1 执行任务开始,时间:1585708427382,num->1
Thread-0 执行任务结束,时间:1585708427707
Thread-1 执行任务结束,时间:1585708428153
Thread-2 执行任务结束,时间:1585708428186
Thread-4 执行任务结束,时间:1585708428442
Thread-3 执行任务结束,时间:1585708428918
Thread-3 完成最后任务,时间:1585708428918
所有线程结束任务,时间: 1585708428918,num->5
上述用例是比较理想的情形,实际使用中为了避免长时间阻塞都会设置等待超时的时间,如果等待超时了会怎么办了?参与CyclicBarrier的多个线程如果有一个或者多个线程因为等待超时或者被中断了都会导致其他处于阻塞状态的线程被唤醒并抛出异常,isBroken方法返回true,此时必须调用reset方法重置,才能重新使用CyclicBarrier,否则会报错BrokenBarrierException,测试用例如下:
static class TimeThread extends Thread {
CyclicBarrier barrier;
long time;
public TimeThread(CyclicBarrier barrier,long time) {
this.barrier = barrier;
this.time=time;
}
@Override
public void run() {
try {
Thread.sleep(time);
System.out.println(getName()+" 执行任务结束,时间:"+System.currentTimeMillis());
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
public void test2() throws Exception {
int threadNum = 6;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务,时间:"+System.currentTimeMillis());
}
});
for(int i = 0; i < threadNum-1; i++) {
new TimeThread(barrier,i*1000).start();
}
try {
barrier.await(3, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
System.out.println("isBroken->"+barrier.isBroken());
//重置以后isBroken变成false,可以继续使用
//必须重置,否则抛出异常BrokenBarrierException
barrier.reset();
System.out.println("after reset,isBroken->"+barrier.isBroken());
}
for(int i = 0; i < threadNum-1; i++) {
new TimeThread(barrier,i*1000).start();
}
barrier.await(5, TimeUnit.SECONDS);
System.out.println("所有线程结束任务,时间: "+System.currentTimeMillis());
barrier.reset();
}
上述用例的执行结果如下:
Thread-0 执行任务结束,时间:1585709228033
Thread-1 执行任务结束,时间:1585709229027
Thread-2 执行任务结束,时间:1585709230032
Thread-3 执行任务结束,时间:1585709231033
isBroken->true
after reset,isBroken->false
Thread-5 执行任务结束,时间:1585709231035
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at synchronizedTest.CyclicBarrierDemo$TimeThread.run(CyclicBarrierDemo.java:80)
java.util.concurrent.TimeoutException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
at synchronizedTest.CyclicBarrierDemo.test2(CyclicBarrierDemo.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at synchronizedTest.CyclicBarrierDemo$TimeThread.run(CyclicBarrierDemo.java:80)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at synchronizedTest.CyclicBarrierDemo$TimeThread.run(CyclicBarrierDemo.java:80)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at synchronizedTest.CyclicBarrierDemo$TimeThread.run(CyclicBarrierDemo.java:80)
Thread-4 执行任务结束,时间:1585709232033
Thread-6 执行任务结束,时间:1585709232036
Thread-7 执行任务结束,时间:1585709233036
Thread-8 执行任务结束,时间:1585709234036
Thread-8 完成最后任务,时间:1585709234036
所有线程结束任务,时间: 1585709234036
CyclicBarrier包含如下属性:
//互斥锁
private final ReentrantLock lock = new ReentrantLock();
/**实现阻塞的Condition*/
private final Condition trip = lock.newCondition();
/**参与的线程数 */
private final int parties;
/* 最后一个到达的线程执行的回调函数 */
private final Runnable barrierCommand;
/**用来描述当前的栅栏的状态,如果其属性为true,表示栅栏已损坏,重新使用该栅栏前需调用reset方法重新创建一个新的Generation */
private Generation generation = new Generation();
/** 尚未到达栅栏,即未调用await方法的线程数 */
private int count;
其中Generation的定义如下:
getNumberWaiting方法返回已经到达栅栏即调用了await方法的线程数,通过parties减去count属性得来;getParties方法返回parties属性,isBroken方法返回generation的broken属性。为啥要用一个私有静态内部类来包裹broken属性而不是直接将其作为CyclicBarrier的私有属性了?这是为了区分当前await动作所属的“栅栏”,每个Generation实例对应一个“栅栏”,比如reset方法和中断线程同时执行,被唤醒的线程就发现CyclicBarrier对应的Generation已经被重置了,这时需要将当前线程中断掉。
构造方法的实现如下:
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
await方法的两个版本最终都是调用dowait方法,该方法是CyclicBarrier的核心,其实现如下:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//获取锁
lock.lock();
try {
final Generation g = generation;
//如果为true,说明上一次调用await方法时有线程抛出异常了而没有调用reset方法重置
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
//如果当前线程处于中断状态,则唤醒所有等待的线程
breakBarrier();
throw new InterruptedException();
}
//count表示未到达栅栏处的线程数,此处是先减1,则赋值给index
int index = --count;
if (index == 0) { // tripped
//index等于0,说明所有线程都到达了栅栏处,当前线程是最后一个到达的线程
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//执行回调函数
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
//如果回调函数执行异常,就会进入此逻辑,唤醒所有等待的线程
if (!ranAction)
breakBarrier();
}
}
//index不等于0
//此处虽然是for循环,但是实际不会循环
for (;;) {
try {
if (!timed)
//如果是无限期等待
trip.await();
else if (nanos > 0L)
//等待指定时间
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//await和awaitNanos方法在线程被中断时才抛出InterruptedException异常,等待超时不会抛出异常而是返回一个负数
//被正常唤醒时返回一个正数,表示等待的时间
if (g == generation && ! g.broken) {
//因为线程中断被唤醒的,g.broken为false
breakBarrier();
throw ie;
} else {
//某个线程准备唤醒了(g.broken为true)但是被打断了,极端情况进入此分支
Thread.currentThread().interrupt();
}
}
//某个线程因被中断或者超时等待导致等待的线程被唤醒,此时g.broken为true
if (g.broken)
throw new BrokenBarrierException();
//正常被唤醒,返回index
if (g != generation)
return index;
if (timed && nanos <= 0L) {
//因为等待超时被唤醒,抛出异常
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
private void breakBarrier() {
generation.broken = true;
count = parties;
//唤醒所有等待的线程
trip.signalAll();
}
private void nextGeneration() {
//唤醒所有线程
trip.signalAll();
//重置
count = parties;
generation = new Generation();
}
用于重置的reset方法的实现如下:
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//唤醒所有线程
breakBarrier(); // break the current generation
//将count和generation等重置
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
因为ReentrantLock的语义和synchronized一样的,所以CyclicBarrier也可用synchronized实现,代码如下:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class ObjectCyclicBarrier {
private static class Generation {
boolean broken = false;
}
private final Object lock = new Object();
private final int parties;
private final Runnable barrierCommand;
private Generation generation = new Generation();
private int count;
private void nextGeneration() {
lock.notifyAll();
count = parties;
generation = new Generation();
}
private void breakBarrier() {
generation.broken = true;
count = parties;
lock.notifyAll();
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
synchronized (lock) {
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();
}
}
boolean timeout=false;
for (; ; ) {
try {
if (!timed)
lock.wait();
else if (nanos > 0L) {
long start=System.currentTimeMillis();
lock.wait(nanos);
if(System.currentTimeMillis()-start==nanos){
timeout=true;
}
}
} catch (InterruptedException ie) {
if (g == generation && !g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && timeout) {
breakBarrier();
throw new TimeoutException();
}
}
}
}
public ObjectCyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
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.toMillis(timeout));
}
public boolean isBroken() {
synchronized (lock) {
return generation.broken;
}
}
public synchronized void reset() {
synchronized (lock) {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
}
}
}
await两个版本的测试用例如下:
@Test
public void test3() throws Exception {
Random random = new Random();
int threadNum = 6;
//回调函数是最后一个调用await,即最后一个到达栅栏的线程执行的
ObjectCyclicBarrier barrier = new ObjectCyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务,时间:" + System.currentTimeMillis());
}
});
Runnable test=new Runnable() {
@Override
public void run() {
try {
String name=Thread.currentThread().getName();
Thread.sleep(random.nextInt(1000));
System.out.println(name + " 到达栅栏A,时间:" + System.currentTimeMillis());
//await方法的返回值可用来确定各线程到达的顺序,0是最后一个到达的
int num = barrier.await();
System.out.println(name + " 执行任务开始,时间:" + System.currentTimeMillis() + ",num->" + num);
Thread.sleep(random.nextInt(2000));
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum - 1; i++) {
new Thread(test).start();
}
int num = barrier.await();
System.out.println("isBroken->" + barrier.isBroken());
System.out.println("所有线程开始执行,时间: " + System.currentTimeMillis() + ",num->" + num);
num = barrier.await();
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis() + ",num->" + num);
}
@Test
public void test4() throws Exception {
Random random = new Random();
int threadNum = 6;
//回调函数是最后一个调用await,即最后一个到达栅栏的线程执行的
ObjectCyclicBarrier barrier = new ObjectCyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务,时间:" + System.currentTimeMillis());
}
});
Runnable test=new Runnable() {
@Override
public void run() {
try {
String name=Thread.currentThread().getName();
Thread.sleep(800+random.nextInt(1000));
System.out.println(name + " 执行任务结束,时间:" + System.currentTimeMillis());
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadNum - 1; i++) {
new Thread(test).start();
}
try {
barrier.await(1500,TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
System.out.println("isBroken->" + barrier.isBroken());
barrier.reset();
}
for (int i = 0; i < threadNum - 1; i++) {
new Thread(test).start();
}
barrier.await();
System.out.println("所有线程结束任务,时间: " + System.currentTimeMillis());
}
其效果和CyclicBarrier一样,甚至代码更加简洁了。