CountDownLatch(倒计时门闩)
倒计时门闩就像一个带计数开关的门,只有在门前等待的线程达到一定数量,门闩才会打开,线程才可以继续执行。
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。CountDownLatch是通过一个计数器来实现的,计数器的初始值为初始任务的数量。每当完成了一个任务后,计数器的值就会减1(CountDownLatch.countDown()方法)。当计数器值到达0时,它表示所有的已经完成了任务,然后在闭锁上等待CountDownLatch.await()方法的线程就可以恢复执行任务。
CountDownLatch的应用场景:
实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。
例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了,例如处理excel中多个表单。
下面通过一段代码演示CountDownLatch用法,
5个初始化线程,6个初始化操作,等操作完成后主线程业务线程才开始自己的工作。
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
private static class InitThread implements Runnable{
@Override
public void run() {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work......");
latch.countDown();//减一
//可以做我们的工作
for(int i =0;i<2;i++) {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ........continue do its work");
}
}
}
private static class BusiThread implements Runnable{
@Override
public void run() {
try {
latch.await();//等待初始化完成
} catch (InterruptedException e) {
}
//做我们自己的工作
for(int i =0;i<3;i++) {
System.out.println("BusiThread_"+Thread.currentThread().getId()
+" do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
SleepTools.ms(1);
//完成第一步操作
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 1st......");
latch.countDown();
//完成第二步操作
System.out.println("begin step 2nd.......");
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 2nd......");
latch.countDown();
}
}).start();
new Thread(new BusiThread()).start();
for(int i=0;i<=3;i++){
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();//等待初始化完成
System.out.println("Main do ites work........");
}
}
Thread_14 ready init work......
Thread_15 ready init work......
Thread_16 ready init work......
Thread_14 ........continue do its work
Thread_14 ........continue do its work
Thread_17 ready init work......
Thread_15 ........continue do its work
Thread_17 ........continue do its work
Thread_16 ........continue do its work
Thread_17 ........continue do its work
Thread_15 ........continue do its work
Thread_16 ........continue do its work
Thread_12 ready init work step 1st......
begin step 2nd.......
Thread_12 ready init work step 2nd......
Main do ites work........
BusiThread_13 do business-----
BusiThread_13 do business-----
BusiThread_13 do business-----
CyclicBarrier(障栅)
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties,Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。
CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。
下面通过一段代码演示CyclicBarrier用法:
共5个子线程,他们全部完成工作后,交出自己结果,再被统一释放去做自己的事情,而交出的结果被另外的线程拿来拼接字符串。
public class UseCyclicBarrier {
private static CyclicBarrier barrier = new CyclicBarrier(5, new CollectThread());
private static ConcurrentHashMap<String,Long> resultMap
= new ConcurrentHashMap<>();//存放子线程工作结果的容器
public static void main(String[] args) {
for(int i=0;i<=4;i++){
Thread thread = new Thread(new SubThread());
thread.start();
}
}
private static class CollectThread implements Runnable{
@Override
public void run() {
StringBuilder result = new StringBuilder();
for(Map.Entry<String,Long> workResult:resultMap.entrySet()){
result.append("["+workResult.getValue()+"]");
}
System.out.println(" the result = "+ result);
//做自己的一些工作
System.out.println("do other business........");
}
}
private static class SubThread implements Runnable{
@Override
public void run() {
long id = Thread.currentThread().getId();
resultMap.put(Thread.currentThread().getId()+"",id);
Random r = new Random();
try {
if(r.nextBoolean()) {
Thread.sleep(1000+id);
System.out.println("Thread_"+id+" ....do something ");
}
barrier.await();
Thread.sleep(1000+id);
System.out.println("Thread_"+id+" ....do its business ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Thread_12 ....do something
Thread_13 ....do something
Thread_14 ....do something
Thread_16 ....do something
the result = [12][13][14][15][16]
do other business........
Thread_12 ....do its business
Thread_13 ....do its business
Thread_14 ....do its business
Thread_15 ....do its business
Thread_16 ....do its business
CyclicBarrier和CountDownLatch的区别
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,CountDownLatch.await一般阻塞主线程,所有的工作线程执行countDown,而CyclicBarrierton通过工作线程调用await从而阻塞工作线程,直到所有工作线程达到屏障。
Exchanger(交换器)
Exchanger(交换器)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
代码演示:
public class UseExchange {
private static final Exchanger<Set<String>> exchange = new Exchanger<Set<String>>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setA = new HashSet<String>();//存放数据的容器
try {
/*添加数据
* set.add(.....)
* */
setA = exchange.exchange(setA);//交换set
/*处理交换后的数据*/
} catch (InterruptedException e) {
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setB = new HashSet<String>();//存放数据的容器
try {
/*添加数据
* set.add(.....)
* set.add(.....)
* */
setB = exchange.exchange(setB);//交换set
/*处理交换后的数据*/
} catch (InterruptedException e) {
}
}
}).start();
}
}