并发编程陷阱系列(八)不要吞食CountDownLatch的线程异常
之前的文章中已经介绍了无处不在的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/
上一篇: JDBC获取新增记录的自增主键