CountDownLatch和CyclicBarrier,都是java.util.concurrent包中同步辅助类,常用的应用场景就是多线程运行计数开关,在指定个数的线程运行完成后,才可去做某件事情。CountDownLatch通过countDown()方法减少执行完成的线程数,而CyclicBarrier是通过await()方法来唤醒等待的线程。以下分别是他们的JavaDoc:
http://www.rritw.com/my/api/jdk-6u10-api-zh/java/util/concurrent/CountDownLatch.html
http://www.rritw.com/my/api/jdk-6u10-api-zh/java/util/concurrent/CyclicBarrier.html
先来看一个关于CyclicBarrier的一个示例,该示例来自于:http://xijunhu.iteye.com/blog/713433
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
/** */
/**
* CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。 CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
*/
public class CyclicBarrierTest {
public static class ComponentThread implements Runnable {
CyclicBarrier barrier;// 计数器
int ID; // 组件标识
int[] array; // 数据数组
// 构造方法
public ComponentThread(CyclicBarrier barrier, int[] array, int ID) {
this.barrier = barrier;
this.ID = ID;
this.array = array;
}
public void run() {
try {
array[ID] = new Random().nextInt(100);
//System.out.println("Component " + ID + " generates: " + array[ID]);
// 在这里等待Barrier处
//System.out.println("Component " + ID + " sleep");
barrier.await();
//System.out.println("Component " + ID + " awaked");
// 计算数据数组中的当前值和后续值
int result = array[ID] + array[ID + 1];
System.out.println("Component " + ID + " result: " + result);
} catch (Exception ex) {
}
}
}
/** */
/**
* 测试CyclicBarrier的用法
*/
public static void testCyclicBarrier() {
for (;;) {
final int[] array = new int[3];
CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
// 在所有线程都到达Barrier时执行,这个方法一定会在所有的等待线程被唤醒之前执行
public void run() {
//System.out.println("testCyclicBarrier run");
array[2] = array[0] + array[1];
}
});
// 启动线程
new Thread(new ComponentThread(barrier, array, 0)).start();
new Thread(new ComponentThread(barrier, array, 1)).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
public static void main(String[] args) {
CyclicBarrierTest.testCyclicBarrier();
}
}
在这个示例中CyclicBarrier声明了两个parites(线程参与者),以及当两个parties的await()都执行后需要执行的一个Runnable命令,它的实现原理就是每个party的await()方法调用时,都去把当前parties的数量减一,然后看当前的parties是否是已经为0,如果为0了,那就执行待执行的runnable线程,否则就进行条件等待(Condition.await()),直到被唤醒为止,被唤醒的线程需要重新获取锁定,也就是说parties的执行顺序就由获取锁定的顺序而确定了,而不一定是谁先执行到await()方法谁先被唤醒。以下是dowait方法中的判断是否所有的parties都已经了await()方法并确定是否执行Runnable的代码:
int index = --count;
//检查当前的parties是否是已经全部都执行了await()方法
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//执行command的方法
if (command != null)
command.run();
ranAction = true;
//唤醒其它其它因为条件不满足(Condition.await())而等待的线程,通过调用Condition.singalAll方法实现,效果同调用获取了锁定方法中的notifyAll()一样
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
方法testCyclicBarrier加了无线循环,可以看到输出component 0和1的输出,会出现交替的情况,这就是因为重新获取锁定的竟争的结果。
这里需要明确一点,Runnable也即barrierCommand一定会在所有的parties都执行完await()立即执行,其它parties中await()之后的代码逻辑一定会等到barrierCommand执行完后才继续执行。
这个和CountDownLatch的实现是不同的,CountDownLatch是在countDown为0的时候,可以执行待执行线程await()之后的代码,但是parties并不会等待它的执行,这里主要是由CPU的分配逻辑确定谁先运行了。下面是通过CountDownLatch对上面的CyclicBarrier的另外一种实现,不过因为他们的实现原理有一点差别,不能够完全达到相同的效果,
import java.util.Random;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch同步辅助类示列,可以简单的理解为计数器。
*/
public class CountDownLatchTest {
public static class ComponentThread implements Runnable {
CountDownLatch latch;// 计数器
int ID; // 组件标识
int[] array; // 数据数组
// 构造方法
public ComponentThread(CountDownLatch latch, int[] array, int ID) {
this.latch = latch;
this.ID = ID;
this.array = array;
}
public void run() {
try {
array[ID] = new Random().nextInt(100);
System.out.println("Component " + ID + " generates: " + array[ID]);
latch.countDown();
System.out.println("Component " + ID + " awaked");
// 计算数据数组中的当前值和后续值
int result = array[ID] + array[ID + 1];
System.out.println("Component " + ID + " result: " + result);
} catch (Exception ex) {
}
}
}
/** */
/**
* 测试用法
*/
public static void test() {
for (;;) {
final int[] array = new int[3];
final CountDownLatch latch = new CountDownLatch(2);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
}
System.out.println("Do Add action");
array[2] = array[0] + array[1];
}
});
thread.start();
// 启动线程
new Thread(new ComponentThread(latch, array, 0)).start();
new Thread(new ComponentThread(latch, array, 1)).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
CountDownLatchTest.test();
}
}
在CountDownLatch,是通过无限循环来判断是否所有的parties都执行了countDown(),如果都执行了或者被中断了就跳出await()方法,继续继续下面的代码,以下是由await()方法调用的、实现判断的方法:
/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
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
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
break;
}
} catch (RuntimeException ex) {
cancelAcquire(node);
throw ex;
}
// Arrive here only if interrupted
cancelAcquire(node);
throw new InterruptedException();
}