zoukankan      html  css  js  c++  java
  • 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里。。

  • 相关阅读:
    MySQL执行计划解读(转载)
    排序算法
    Linux下在防火墙中开启80端口、3306端口
    Android APN
    PB之——DropDownListBox 与 DropDownPictureListBox
    CSS总则。
    WIN7系统中设置默认登录用户
    Javascript日期比较
    myeclipse中UTF-8设置
    webview loadUrl() 弹出系统浏览器解决办法
  • 原文地址:https://www.cnblogs.com/shamo89/p/6709548.html
Copyright © 2011-2022 走看看