java并发之CountDownLatch
一、简介
CountDownlatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
它本身而言是Java并发包中非常有用的一个类,它可以让某些任务完成以后再继续运行下面的内容,每个任务本身执行完毕后让计数器减一,直到计数器清零后,以下的内容才可以继续运行,否则将阻塞等待。
二、应用场景
想了一下,这个场景非常适合用于项目中这样的场景: 我们有个项目,它需要三个第三方的API,并把结果拿到,在一个线程中顺序去拿结果没有问题,但是这里这三个任务是非常耗时的操作,如果顺序获取性能非常差,因此可以考虑用三个线程,当三个线程拿到结果后才继续主线程的工作,等三个线程运行结束后,由主线程去取子线程运行的结果。 这里有个很重要的前提:我们的系统运行在4个cpu的server上,这样多线程才能体现性能,JVM会分配这些线程尽量运行在不同的cpu上。
CountDownLatch有以下基本方法:
1)await(),阻塞等待,直到计数器清零
2)await(int timeout, TimeUnit unit),使线程阻塞,除非被中断或者超过等待的最大时间。如果达到计数器清零,则await返回true,如果等待超过了最大的等待时间,则返回false。
3)countDown(),计数器减一,当计数器清零时,await的线程被唤醒,线程继续执行
4)getCount(),获取当前计数器的大小
代码示例
现在模拟一个业务场景,就是页面上的列表数据导出,通常的做法是根据查询条件从数据库,然后把查询的数据再逐个写到excel文件里,正常情况这样做是没问题的,但是如果数据量很大,还在一个线程里操作的话,将会非常耗时。这时我们可以根据实际情况,比方说新建三个线程,这三个线程把整个要处理的数据拆分成三段分别同时处理,然后主线程等这三个线程都处理完了再进行整合这三部分的操作。
那么问题来了,怎么才能确保主线程始终能等这三个业务线程执行完了再执行呢?
1 public class Test {2
3 public static void main(String[] args) throws IOException {
4 // 模拟数据库资源
5 CopyOnWriteArrayList<Integer> ziyuan = new CopyOnWriteArrayList<Integer>();
6 for (int i = 1; i < 10; i++) {
7 ziyuan.add(i);
8 }
9 ExecutorService executorService = Executors.newCachedThreadPool();
10 // 标记一共有ziyuan.size()/3个线程需要等待
11 CountDownLatch cLatch = new CountDownLatch(ziyuan.size()/3);
12 // 开启三个线程
13 for (int i = 0; i < ziyuan.size()/3; i++) {
14 executorService.execute(new TaskRunn(ziyuan,cLatch, 3*i));
15 }
16 // 关闭线程池
17 executorService.shutdown();
18 // 主线程等待
19 try {
20 cLatch.await();
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 }
24 System.out.println("我是主线程要操作的内容,必须确保我在其它三个线程执行完后最后打印。。。");
25 }
26 }
27
28 class TaskRunn implements Runnable{
29
30 private CopyOnWriteArrayList<Integer> ziyuan;
31 private CountDownLatch cLatch;
32 private int c;
33 public TaskRunn(CopyOnWriteArrayList<Integer> ziyuan,CountDownLatch cLatch,int c) {
34 this.ziyuan = ziyuan;
35 this.cLatch = cLatch;
36 this.c = c;
37 }
38
39 @Override
40 public void run() {
41 System.out.println(Thread.currentThread().getName()+"begin...");
42 for (int i = c; i < ziyuan.size()/3+c; i++) {
43 System.out.println(Thread.currentThread().getName()+"//"+ziyuan.get(i)+"//CountDownLatch:"+cLatch.getCount());
44 try {
45 Thread.sleep(new Random().nextInt(4)*1000);// 模拟业务耗时
46 } catch (InterruptedException e) {
47 e.printStackTrace();
48 }
49 }
50 System.out.println(Thread.currentThread().getName()+"end...");
51 // 每个业务线程执行完后会减去1
52 cLatch.countDown();
53 }
54
55 }
如果把上面代码红色字体部分注释掉,那结果就不会这样了。。。
注意点
在实际开发中,为了防止发生异常时cLatch.countDown()没有执行到,所以最好把它放到finally里。。
以上是 java并发之CountDownLatch 的全部内容, 来源链接: utcz.com/z/394212.html