一、CountDownLatch类语法要点

构造函数接收计数器的值。

任务线程执行完毕后,记得调用一次 latch.countDown()方法,提示CountDownLatch对象,当前线程已经执行完毕。

在需要同步的地方,使用 latch.await()方法进行阻塞。再次恢复任务执行,需要CountDownLatch的计数器为0。

         缺点:CountDownLatch这个类的缺点就很明显,如果子线程耗时过多,那么主线程也会一直等待,程序执行效率大大降低。

         应用场景:

开始执行任务前,等待 N 个前置线程完成各自的准备任务:例如应用程序启动类要确保在处理用户请求前,所有 N 个外部系统已经启动和运行了。

        有一种用法是new CountDownLatch(1),其它多个线程中调用 countDownLatch.await();在主函数中调用countDown(),然后其它阻塞的大量线程同时执行。

         另外用法示例:

        

import java.util.concurrent.CountDownLatch;

/**
* Created by jay.zhou on 2018/9/4.
*/
public class CountDownLatchDemo {
private static final int THREAD_COUNT_NUM = 7;
private static CountDownLatch latch = new CountDownLatch(THREAD_COUNT_NUM);

public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < THREAD_COUNT_NUM; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "执行");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每个线程执行完毕,都把计数器减1
latch.countDown();
}, "Thread" + i).start();
}

//CountDownLatch.await()阻塞主函数。
//那么,主函数需要等到 latch.countDown()被调用七次后,方可恢复执行
latch.await();
System.out.println("主函数阻塞结束");
/**
* Thread1执行
Thread3执行
Thread5执行
Thread0执行
Thread2执行
Thread4执行
Thread6执行
主函数阻塞结束
*/

}
}

        

二、CyclicBarrier类语法要点

构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量。

CyclicBarrier.await() 方法告诉 CyclicBarrier 我已经到达了屏障,任务执行完毕,等待其它任务的完成。然后当前线程被阻塞。

所有线程都 await()了,那么将会执行CyclicBarrier的构造函数中执行的任务。

package com.ssi.javaSE;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
* Created by jay.zhou on 2018/9/4.
*/
public class CyclicBarrierDemo {
private static final int THREAD_COUNT_NUM = 4;
public static void main(String[] args) {
//创建一个Runnable接口实现
Runnable task = () -> System.out.println("执行指定任务");
//创建一个CyclicBarrier
CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT_NUM , task);

for (int i = 0; i < THREAD_COUNT_NUM; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "执行任务完毕");
//我任务完成了,所以我等待了
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "Thread" + i).start();
}

/**
* Thread0执行完毕
Thread2执行完毕
Thread1执行完毕
Thread3执行完毕
执行指定任务
*/
}
}

三、使用场景

         为了提高一些查询数据库的性能,可以将一个查询分为多个线程去操作数据库。

         如果我们操作一个非常耗时的数据库操作的时候,例如一个查询,我需要把它分为多个线程分别去查询,等待所有的线程查询完之后,然后需要组装查询完的数据。

         例如,有一个查询条件集 List<Key>,目标是获取到Key对应的Value。所以,客户端希望得到的结果是Map<Key,Value>。

         思路:

         (1)先把List<Key>拆分成多个集合。  List<Key1> , List<Key2>,List<Key3>,满足 拆分的三个集合的size()等于List<Key>的size()。

         (2)创建一个CyclicBarrier对象,设置构造函数为3。

         (3)创建查询结果集,Map<Key,Value> map

        (4)同时开启三个线程,每个线程的任务就是根据 List<Key1>、List<Key2>、List<Key3>,查询出Map<Key1,Value1>、Map<Key2,Value2>、Map<Key3,Value3>。每个线程的任务执行完毕,调用CyclicBarrier对象的await()方法,等待其它线程执行完毕。

        (5)上述三个任务线程执行完毕之后,也就是准备任务都完成了。接着,CyclicBarrier执行构造函数中接收的Task。

               将任务执行的结果进行组装。将Map<Key1,Value1>、Map<Key2,Value2>、Map<Key3,Value3> 的值组装到 结果集 Map<Key,Value> map 中 ,完成最终的目标。

 

四、CyclicBarrier 和 CountDownLatch 的区别

 

         CountDownLatch 调用 await()方法后,会在此处阻塞。而CyclicBarrier不会阻塞线程,它只会在其它任务线程任务都执行完毕之后,执行构造函数中接收的任务。

可以重置计数器,并让线程们重新执行一次。