在Java的并发库中,有两个并不是经常使用但却非常好用的线程同步工具,分别为CyclicBarrier和CountDownLatch。下面分别对这两个工具进行介绍:
CyclicBarrier
CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
CyclicBarrier的使用方法:通过CyclicBarrier的await()来阻塞线程,实现线程屏障。当某一线程运行到CyclicBarrier的await()方法时,该线程将会被阻塞,直到指定个数的线程调用了await()方法,屏障才会放行。放行所需要的线程个数取决于创建CyclicBarrier对象(调用构造方法)时指定的参数。
CyclicBarrier的主要方法如下:
/*构造方法。当线程调用await()方法后将开始等待,当指定个数的线程调用了await()方法后,
所有调用了await()的线程继续运行。*/
CyclicBarrier(int parties):
//当await的数量到达了设定的数量后,首先执行该Runnable对象。
CyclicBarrier(int parties,Runnable barrierAction):
//通知barrier已完成线程
void await():
//查看当前等待线程的个数
int getNumberWaiting();
具体实例:下面的代码实现了开启三个线程,每个线程都有三个检查点,每当一个程序到达一个检查点,必须等待其他两个线程都到达了这一检查点方能继续运行。
package com.fhp.testutils;
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestCyclicBarrier {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CBBusiness business = new CBBusiness();
for(int i = 0; i < 3; i++) {
service.execute(new Runnable() {
@Override
public void run() {
business.execute();
}
});
}
service.shutdown();
}
}
class CBBusiness {
private final CyclicBarrier checkPoint = new CyclicBarrier(3);
public void execute() {
try {
Random rand = new Random();
Thread.sleep(rand.nextInt(5000));
System.out.println("Checkpoint 1: " + checkPoint.getNumberWaiting());
checkPoint.await();
Thread.sleep(rand.nextInt(5000));
System.out.println("Checkpoint 2: " + checkPoint.getNumberWaiting());
checkPoint.await();
Thread.sleep(rand.nextInt(5000));
System.out.println("Checkpoint 3: " + checkPoint.getNumberWaiting());
checkPoint.await();
} catch(Exception e) {
e.printStackTrace();
}
}
}
运行结果:
Checkpoint 1: 0
Checkpoint 1: 1
Checkpoint 1: 2
Checkpoint 2: 0
Checkpoint 2: 1
Checkpoint 2: 2
Checkpoint 3: 0
Checkpoint 3: 1
Checkpoint 3: 2
应用场景:在某种需求中,比如一个大型的任务,常常需要分配很多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择CyclicBarrier了。
CountDownLatch
先引入一下官方的定义:CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
实际上,CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行.利用这种特性,可以让主线程等待子线程的结束。
CountDownLatch的主要方法:
//构造方法,指定计数器的初始值。
CountDownLatch(int count);
//令线程等待,直至计数器归零为止。
void await();
//令计数器减1,归零后等待线程将继续执行。
void countDown();
//查看当前计数器的值
long getCount();
具体实例:模拟运动员进行田径跑步比赛的场景。每个运动员需要先在起点就位,待所有运动员就位后,裁判打响发令枪,宣布比赛开始,待所有运动员到达终点后,裁判宣布比赛结束。
package com.fhp.testutils;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestCountDownLatch {
public static void main(String[] args) {
final CDLBusniness business = new CDLBusniness(5);
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
try {
business.referee();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
for(int i = 1; i <= 5; i++) {
final int current = i;
final Random r = new Random();
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(r.nextInt(1500));
business.athlete(current);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
service.shutdown();
}
}
class CDLBusniness {
private final CountDownLatch athlete;
private final CountDownLatch start = new CountDownLatch(1);
private final CountDownLatch goal;
public CDLBusniness(int numOfAthlete) {
this.athlete = new CountDownLatch(numOfAthlete);
this.goal = new CountDownLatch(numOfAthlete);
}
//运动员的动作
public void athlete(int id) throws InterruptedException {
athlete.countDown();
System.out.println("Athlete " + id + " has been in place.");
start.await();
System.out.println("Athlete " + id + " is running.");
Random r = new Random();
Thread.sleep(1000 + r.nextInt(3000));
System.out.println("Athlete " + id + " has reached the goal!");
goal.countDown();
}
//裁判的动作
public void referee() throws InterruptedException {
System.out.println("Referee has been in place.");
athlete.await();
Random r = new Random();
Thread.sleep(r.nextInt(2000));
start.countDown();
System.out.println("Referee: Match start!");
goal.await();
System.out.println("Referee: Match end!");
}
}