并发编程陷阱系列(八)不要吞食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/

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值