CyclicBarrier 和 CountDownLatch 都可以用來讓一組線程等待其它線程款侵。與 CyclicBarrier 不同的是,CountdownLatch 不能重新使用
CountDownLatch
- 一個同步輔助類捞镰,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待。
- 用給定的計數(shù)初始化CountDownLatch鄙漏。
- 由于調(diào)用了countDown()方法深碱,所以在當前計數(shù)到達零之前腹鹉,await方法會一直受阻塞。
- 之后敷硅,會釋放所有等待的線程功咒,await 的所有后續(xù)調(diào)用都將立即返回愉阎。
- 這種現(xiàn)象只出現(xiàn)一次——計數(shù)無法被重置。
- 一個線程(或者多個)力奋, 等待另外N個線程完成某個事情之后才能執(zhí)行
簡單使用
import java.util.concurrent.*;
/**
* Created by chengkang
* 2018/5/26 下午2:38
*/
class Demo implements Runnable{
String name;
CountDownLatch countDownLatch;
public Demo(String name, CountDownLatch countDownLatch){
this.name = name;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println(this.name+"---開始運行");
try {
Thread.sleep(1000);
System.out.println(this.name+"---運行結(jié)束");
countDownLatch.countDown();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(2);
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(new Demo("XXQ", countDownLatch));
pool.execute(new Demo("CK", countDownLatch));
System.out.println("main線程執(zhí)行中......");
try {
countDownLatch.await();
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("main線程繼續(xù)......");
pool.shutdown();
}
}
output:
XXQ---開始運行
CK---開始運行
main線程執(zhí)行中......
XXQ---運行結(jié)束
CK---運行結(jié)束
main線程繼續(xù)......
代碼分析:
- 當主線程把“XXQ”和“CK”兩個線程都提交后榜旦,主線程調(diào)用countDownLatch.await()方法將自己阻塞
- 當其他線程調(diào)用countDownLatch.countDown()達到約定次數(shù)后,主線程才能被喚醒繼續(xù)
CountDownLatch的使用場景:
- 在一些應用場合中景殷,需要等待某個條件達到要求后才能做后面的事情溅呢;
- 同時當線程都完成后也會觸發(fā)事件,以便進行后面的操作猿挚。
CountDownLatch最重要的方法是countDown()和await()咐旧,前者主要是倒數(shù)一次,后者是等待倒數(shù)到0绩蜻,如果沒有到達0铣墨,就只有阻塞等待了。
CyclicBarrier
- 假設(shè)有一個場景辜羊,每個線程代表一個跑步的運動員踏兜,當運動員都準備好之后,才一起出發(fā)八秃,只要有一個運動員還沒有準備好碱妆,所有線程就一起等待。
定義
- CyclicBarrier 的字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier)昔驱。
- 它要做的事情是疹尾,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時骤肛,屏障才會開門纳本,所有被屏障攔截的線程才會繼續(xù)干活。
- CyclicBarrier默認的構(gòu)造方法是CyclicBarrier(int parties)腋颠,其參數(shù)表示屏障攔截的線程數(shù)量繁成,每個線程調(diào)用await方法告訴CyclicBarrier我已經(jīng)到達了屏障,然后當前線程被阻塞淑玫。
簡單使用
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by chengkang
* 2018/8/18 下午8:11
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
ExecutorService pool = Executors.newCachedThreadPool();
try{
pool.execute(new TRunnable("XXQ", cyclicBarrier));
pool.execute(new TRunnable("CK", cyclicBarrier));
pool.execute(new TRunnable("XCK", cyclicBarrier));
}finally {
pool.shutdown();
}
}
static class TRunnable implements Runnable{
String name;
CyclicBarrier cyclicBarrier;
public TRunnable(String name, CyclicBarrier cyclicBarrier){
this.name = name;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(100* new Random().nextInt(100));
System.out.println(this.name+"已經(jīng)準備好了.....");
cyclicBarrier.await();
} catch(InterruptedException e) {
e.printStackTrace();
} catch(BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(this.name+"出發(fā)=硗蟆!絮蒿!");
}
}
}
output:
CK已經(jīng)準備好了.....
XXQ已經(jīng)準備好了.....
XCK已經(jīng)準備好了.....
XCK出發(fā)W鸢帷!土涝!
CK出發(fā)7鹗佟!但壮!
XXQ出發(fā)<叫骸3B隆!
CyclicBarrier 分析結(jié)果
- 上述程序我們創(chuàng)建了一個線程池腔长,這個線程池中有三個線程袭祟,每個線程都傳遞了一個相同的CyclicBarrier對象和運動員的名字
- 我們TRunnable類中的run方法使每一個進來的運動員都休眠0.1-10秒的時間,然后調(diào)用await()方法捞附,就是說每個線程進來都需要進行等待巾乳,直到所有的CyclicBarrier 都處于準備好了的狀態(tài),所有線程才能統(tǒng)一開始執(zhí)行鸟召!
CyclicBarrier 使用場景
- CyclicBarrier可以用于多線程計算數(shù)據(jù)胆绊,最后合并計算結(jié)果的應用場景。
- 比如我們用一個Excel保存了用戶所有銀行流水欧募,每個Sheet保存一個帳戶近一年的每筆銀行流水压状,現(xiàn)在需要統(tǒng)計用戶的日均銀行流水,先用多線程處理每個sheet里的銀行流水跟继,都執(zhí)行完之后种冬,得到每個sheet的日均銀行流水,最后舔糖,再用barrierAction用這些線程的計算結(jié)果娱两,計算出整個Excel的日均銀行流水。
CyclicBarrier和CountDownLatch的區(qū)別
- CountDownLatch簡單的說就是一個線程等待金吗,直到他所等待的其他線程都執(zhí)行完成并且調(diào)用countDown()方法發(fā)出通知后十兢,當前線程才可以繼續(xù)執(zhí)行。
- cyclicBarrier是所有線程都進行等待摇庙,直到所有線程都準備好進入await()方法之后旱物,所有線程同時開始執(zhí)行!
- CountDownLatch的計數(shù)器只能使用一次卫袒。而CyclicBarrier的計數(shù)器可以使用reset() 方法重置宵呛。所以CyclicBarrier能處理更為復雜的業(yè)務(wù)場景,比如如果計算發(fā)生錯誤夕凝,可以重置計數(shù)器烤蜕,并讓線程們重新執(zhí)行一次。
- CyclicBarrier還提供其他有用的方法迹冤,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數(shù)量。isBroken方法用來知道阻塞的線程是否被中斷虎忌。如果被中斷返回true泡徙,否則返回false。