之前的面试有被提到过java.util.concurrent中解决并发的问题。
看Thinking in Java 学习一下其中一些构件的使用。
本文记录关于CountDownLatch 和 CyclicBarrier 使用的小例子。
CountDownLatch 典型用法就是将一个程序分成n个相互独立的可解决的任务(锁存器)
public class CountDownLatchDemo {
static final int SIZE = 10;
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool();
// All must share a single CountDownLatch object
CountDownLatch latch = new CountDownLatch(SIZE);// 初始化CountDownLatch,并初始化计数值为100
for (int i = 0; i < 10; i++) {
exec.execute(new WaitingTask(latch));
}
for (int i = 0; i < SIZE; i++) {
exec.execute(new TaskPortion(latch));
}
System.out.println("Launched all tasks");
exec.shutdown();// Quit when all tasks complete
}
}
注意:
- CountDownLatch 被设计只能触发一次,初始化时要传入一个初始计数值
- 初始计数值不能被重置,当任务完成时,这个锁存器调用 CountDown() 方法来减小计数值
- 等待被解决的任务调用 latch 的 await() 方法进入阻塞,直到正在进行的任务的计数器为 0
class TaskPortion implements Runnable {
private static int counter = 0;
private final int id = counter++;
private static Random rand = new Random(47);
private final CountDownLatch latch;
public TaskPortion(CountDownLatch latch) {// must initialize latch in constructor because of final latch
// TODO Auto-generated constructor stub
this.latch = latch;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
doWork();
latch.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void doWork() throws InterruptedException {
// TODO Auto-generated method stub
TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));// rand.nextInt() is safe
System.out.println(this + "completed");
}
@Override
public String toString() {
// TODO Auto-generated method stub
return String.format("%1$-3d ", id);
}
}
注意:
- TaskPortion 的 doWork() 方法随机休眠一段时间来模拟部分工作的完成
- 当模拟任务完成后,计数值减一
- TaskPortion 内包含一个静态 Random 对象,有可能多线程同时调用 Random.nextInt() ,但它是线程安全的。当然可以移除 static 限定符来解决
class WaitingTask implements Runnable {
private static int counter = 0;
private final int id = counter++;
private final CountDownLatch latch;
public WaitingTask(CountDownLatch latch) {
// TODO Auto-generated constructor stub
this.latch = latch;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
latch.await();
System.out.println("Latch barrier passed for " + this.toString());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(this + " interrupted");
}
}
@Override
public String toString() {
// TODO Auto-generated method stub
return String.format("WaitingTask %1$-3d ", id);
}
}
注意:
- WaitingTask 调用了 latch 的 await() 方法,会等待 TaskPortion 中计数值将为 0 在执行。
CyclicBarrier 可以多次重用,下一步骤在当前步骤完成前等待。
public class HorseRace {
static final int FINISH_LINE = 10;
private List<Horse> horses = new ArrayList<>();
private ExecutorService exec = Executors.newCachedThreadPool();
private CyclicBarrier barrier;
public HorseRace(int nHorses, final int pause) {
// TODO Auto-generated constructor stub
barrier = new CyclicBarrier(nHorses, new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
StringBuilder s = new StringBuilder();
for (int i = 0; i < FINISH_LINE; i++) {
s.append("=");// The fence on the racetrack
}
System.out.println(s);
for(Horse horse : horses){
System.out.println(horse.tracks());
}
for(Horse horse : horses)
if (horse.getStrides() >= FINISH_LINE) {
System.out.println(horse + "won!");
exec.shutdownNow();
return;
}
try {
TimeUnit.MILLISECONDS.sleep(pause);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("barrier-action sleep interrupted");
}
}
});
for (int i = 0; i < nHorses; i++) {
Horse horse = new Horse(barrier);
horses.add(horse);
exec.execute(horse);
}
}
public static void main(String[] args){
int nHorses = 3;
int pause = 200;
if (args.length > 0) {
int n = new Integer(args[0]);
nHorses = n > 0 ? n : nHorses;
}
if (args.length > 1) {
int p = new Integer(args[1]);
pause = p > 0 ? p : pause;
}
new HorseRace(nHorses, pause);
}
}
注意:
- 向 CyclicBarrier 提供了一个“栅栏动作”,即一个Runnable,以匿名内部类的形式创建;
- Runnable 的动作当计数值(此例中为马的数量 nHorse)到达 0 时 自动执行
- FINISH_LINE 定义了赛道长度,打印“=”
- 打印各匹马的轨迹,当有马大于等于赛道长度时,停止所有任务(shutdownNow() , 与 shutdown() 的区别)
- 每一个 Horse 对象都由一个线程控制,来控制进度轨迹。
class Horse implements Runnable {
private static int counter = 0;
private final int id = counter++;
private int strides = 0;
private static Random rand = new Random(47);
private static CyclicBarrier barrier;
public Horse(CyclicBarrier barrier) {
// TODO Auto-generated constructor stub
this.barrier = barrier;
}
public synchronized int getStrides(){return strides;}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
synchronized(this){
strides += rand.nextInt(3); // Produces 0, 1, or 2
}
barrier.await();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// An legitimate way to exit
e.printStackTrace();
} catch (BrokenBarrierException e){
// This is the way we want to know about
throw new RuntimeException(e);
}
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Horse " + id + " ";
}
public String tracks(){
StringBuilder s = new StringBuilder();
for (int i = 0; i < getStrides(); i++) {
s.append("*");
}
s.append(id);
return s.toString();
}
}