多線程之CyclicBarrier

前言
昨天我們分享了多線程里面的一個(gè)計(jì)數(shù)器countDownLatch,它的主要作用是控制線程執(zhí)行順序,確保上一個(gè)操作完成后,下一個(gè)線程才能啟動(dòng)運(yùn)行,但是某些情況下countDownLatch并不能滿足我們的需求,比如執(zhí)行A線程10次后,我們需要執(zhí)行B線程,然后再執(zhí)行A線程10次,循環(huán)往復(fù),為了應(yīng)付這樣的應(yīng)用場(chǎng)景,jdk也為我們提供了相應(yīng)的解決方案——另一個(gè)計(jì)數(shù)器CyclicBarrier,今天我們就一起來看看吧。
CyclicBarrier
CyclicBarrier中文的意思是循環(huán)格柵,循環(huán)屏障,循環(huán)關(guān)卡,循環(huán)分界線,我覺得叫循環(huán)分界線應(yīng)該更好理解,因?yàn)樗鸬淖饔镁褪欠指簟K?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">countDownLatch一樣,也是jdk1.5引入的。它的作用有點(diǎn)像觸發(fā)器,當(dāng)達(dá)到設(shè)定的數(shù)值時(shí),我們可以觸發(fā)一個(gè)操作。
這樣干巴巴講,大家可能想不來,那我們先看這樣一段示例代碼:
public class Example {
static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
AtomicInteger count2 = new AtomicInteger(0);
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
System.out.println("多線程循環(huán)完成" + count2.getAndIncrement());
});
for (int i = 0; i < 100; i++) {
new Thread(new Task(cyclicBarrier)).start();
}
}
static class Task implements Runnable {
private final CyclicBarrier cyclicBarrier;
public Task(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
synchronized (count) {
System.out.println(count.getAndIncrement());
}
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
上面的代碼中,我們定義了一個(gè)原子整數(shù),初始值為0;
我們定義了一個(gè)cyclicBarrier,觸發(fā)值我們?cè)O(shè)定為10,在觸發(fā)操作里我們打印提示信息;
我們還定義了一個(gè)線程,在線程內(nèi)部我們對(duì)原子整數(shù)count加一并打印。
然后我們?cè)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">main方法中循環(huán)啟動(dòng)100個(gè)線程,運(yùn)行上面的代碼,結(jié)果大致如下:

根據(jù)運(yùn)行結(jié)果,我們可以看出來,count每增加10,也就是啟動(dòng)十個(gè)線程,會(huì)觸發(fā)CyclicBarrier中我們定義的操作,這個(gè)數(shù)值也就是我們?cè)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">CyclicBarrier指定的觸發(fā)值。
如果我們把觸發(fā)值設(shè)置為5,那應(yīng)該每隔5次就會(huì)打印一次,我們驗(yàn)證下:

事實(shí)也確實(shí)如此,我想到這里大家應(yīng)該都清楚CyclicBarrier的用法了吧。
關(guān)于CyclicBarrier的構(gòu)造方法我在多說兩句,CyclicBarrier有兩種構(gòu)造方法,但是第二種最常用,也就是我們演示的這種:

入?yún)⒂袃蓚€(gè),一個(gè)就是觸發(fā)次數(shù),一個(gè)就是觸發(fā)操作。
另外還需要注意的是,它的await()方法和countDownLatch的方法是不一樣的,在它的await()方法中,有一個(gè)--count的操作,也就是每次都會(huì)把我們?cè)O(shè)定的數(shù)值減一,直至為零,如果--count為0,它就會(huì)觸發(fā)我們的barrierAction:

對(duì)于它的應(yīng)用場(chǎng)景,我想大家應(yīng)該能夠想到很多,比如固定條數(shù)保存數(shù)據(jù),多線程提交保存操作,然后達(dá)到固定條數(shù)提交數(shù)據(jù)庫(kù)保存,當(dāng)然還有很多其他的應(yīng)用場(chǎng)景,大家可以結(jié)合自己的應(yīng)用需求,好好想一想。
總結(jié)
雖然一開始我拿countDownLatch和CyclicBarrier做比較,但是事實(shí)上,它們兩個(gè)不具備任何可比性,而且適用的場(chǎng)景也是不一樣的。countDownLatch核心方法是countDown和aWait,主要用于控制線程執(zhí)行順序;CyclicBarrier主要用于有回調(diào)需求的場(chǎng)景,而且它的await方法也不一樣,但是它們都很有用。
最后,希望大家多動(dòng)手實(shí)現(xiàn),多練習(xí),畢竟學(xué)習(xí)這件事,還是實(shí)踐出真知,多線程想要學(xué)的好,juc下面的類少不了,一起加油吧!
