之前的文章中已经介绍了无处不在的InterruptedException的处理方式了,使用CountDownLatch也会有类似的问题(正确的处理方式见下面代码: Thread.currentThread().interrupt()),顺便复习下CountDownLatch的使用方法。
在一些应用中,有多个线程,某个线程会在其他线程执行完毕之后才开始执行。
比如,想象有一个程序先下载一堆网页,压缩然后通过EMAIL发送出去。如果要用多线程来实现,压缩网页的程序不能在下载完成后启动。
如何处理呢?一个非常简便的方法就是使用java.util.concurrent中的CountDownLatch。
在CountDownLatch中,你可以定义一个数字,每次操作完成后数字都会减一。如果所有操作都完成了,这个数字变成0,使用同一个CountDownLatch作为同步工具的其他线程可以调用await方法完成任务。
然我们看一个简单的例子,第一个类是一个简单的Runnable。在我们的例子中,这个类没什么用处,只是采取随机休眠的方式模拟一些任务的执行。
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class Worker implements Runnable {
private CountDownLatch countDownLatch;
public Worker(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Thread.sleep(getRandomSeconds()); // sleep random time to simulate long running task
System.out.println("Counting down: " + Thread.currentThread().getName());
this.countDownLatch.countDown();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
// returns a long between 0 and 9999
private long getRandomSeconds() {
Random generator = new Random();
return Math.abs(generator.nextLong() % 10000);
}
}
唯一有意思的是这一行调用:
this.countDownLatch.countDown();
一旦任务执行完毕,countDownLatch中的计数器就会减一。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WorkManager {
private CountDownLatch countDownLatch;
private static final int NUMBER_OF_TASKS = 5;
public WorkManager() {
countDownLatch = new CountDownLatch(NUMBER_OF_TASKS);
}
public void finishWork() {
try {
System.out.println("START WAITING");
countDownLatch.await();
System.out.println("DONE WAITING");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
public void startWork() {
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_TASKS);
for (int i = 0; i < NUMBER_OF_TASKS; i++) {
Worker worker = new Worker(countDownLatch);
executorService.execute(worker);
}
executorService.shutdown();
}
public static void main(String[] args) {
WorkManager workManager = new WorkManager();
System.out.println("START WORK");
workManager.startWork();
System.out.println("WORK STARTED");
workManager.finishWork();
System.out.println("FINISHED WORK");
}
}
startWork方法使用ExecutorService执行Runnable类。
在finishWork方法中,await方法会一直等待,直到CountDownLatch内部的计数器为0时,await后的方法才会继续执行下去。
执行结果:
START WORK WORK STARTED START WAITING Counting down: pool-1-thread-3 Counting down: pool-1-thread-4 Counting down: pool-1-thread-1 Counting down: pool-1-thread-5 Counting down: pool-1-thread-2 DONE WAITING FINISHED WORK
参考:http://markusjais.com/how-to-use-java-util-concurrent-countdownlatch/