今天過生日啦义辕,今年的生日比往年晚一些(閏了個四月)又漲一歲历谍,29啦现拒。
晚上閑著讀會CyclickBarrier代碼,中文意思翻譯過來叫循環(huán)柵欄望侈,顧名思義我們可以理解為賽馬跑圈具练,不過有點(diǎn)特色就是這些賽馬可以一圈又一圈的跑。并且所有馬跑完再進(jìn)行下一圈。
先寫一個小程序把玩一下吧。
程序目的如下:
我們模擬一個學(xué)校假設(shè)有三個班級及刻,每個班級有5個學(xué)生,同學(xué)們出了期末成績陵究,我們希望通過三個人(線程)幫我排序每個班學(xué)生的成績,順便就用冒泡做個簡單實(shí)現(xiàn)吧奥帘。就用最粗暴的雙層循環(huán)不做優(yōu)化铜邮,因?yàn)閮?yōu)化的話其實(shí)冒泡排序還能做一些有序邊界的優(yōu)化,此文不做探討,還請各位看官輕噴松蒜。
talk is cheap 扔茅,show me the code
/**
* @author guozc
*
* 2020年7月1日
*/
public class CyclicBarrierTest {
private static int[] chengji = new int[15];
static class TaskThread extends Thread {
CyclicBarrier barrier;
int beginIndex;
int endIndex;
public TaskThread(CyclicBarrier barrier, int beginIndex) {
this.barrier = barrier;
this.beginIndex = beginIndex;
this.endIndex = beginIndex + 5;
}
@Override
public void run() {
try {
for(int t = 1; t < 6; t++) {
for(int i = beginIndex; i < endIndex-1; i++) {
if (chengji[i] > chengji[i + 1]) {
int temp = chengji[i + 1];
chengji[i + 1] = chengji[i];
chengji[i] = temp;
}
}
System.out.println(Thread.currentThread().getName() +"完成了第" + t +"次排序");
barrier.await();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Random random = new Random();
for (int i = 0; i < 15; i++) {
chengji[i] = random.nextInt(50)+50;
}
System.out.println("原始成績"+Arrays.toString(chengji));
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
int times = 1;
@Override
public void run() {
System.out.println("執(zhí)行完第" + times+"次");
System.out.println(Arrays.toString(chengji));
times++;
}
});
for(int i = 0; i < 3; i++) {
new TaskThread(cyclicBarrier,i*5).start();
}
}
}
初始化一個容量為3的循環(huán)柵欄,外加一個全班級成績的隨機(jī)數(shù)組秸苗,main方法入口一開始初始化這15個人的成績召娜,然后我們?nèi)藶榈姆譃槿M,交給每個線程不同的分段去操作屬于自己的班級成績惊楼,通過冒泡排序進(jìn)行成績排名玖瘸。為了方便演示阻塞效果我們在每次冒泡排完一次就輸出一下當(dāng)前線程名字并在線程內(nèi)部初始化了一個統(tǒng)計變量讓輸出更加清晰
代碼執(zhí)行結(jié)果如下
一開始分了三組也就是結(jié)果的第一行原始成績單,因?yàn)槭请S機(jī)的所以是無序的檀咙,通過每個線程撞第一次柵欄前的運(yùn)算我們看到第一次執(zhí)行結(jié)果雅倒,每一組的最后一個變成了最大的,依次類推完全符合冒泡排序的規(guī)律弧可,直接看最后依次蔑匣,我們看到每一組我順序都是從小到大進(jìn)行展示的。符合我們的預(yù)期棕诵。
在日常開發(fā)中殖演,循環(huán)柵欄一般用于并發(fā)運(yùn)算,比如我們稍微改一下這個場景年鸳,要求計算每個班級總分?jǐn)?shù),或者平均分?jǐn)?shù)丸相,那么久可以開啟三個線程去執(zhí)行搔确,當(dāng)然這么用有點(diǎn)小題大做了,因?yàn)橛嬎沩樞蚝芸烀鹬遥瑪?shù)據(jù)量又小所以可能還不如單線程快膳算。但是這部妨礙我們理解這個模型。因?yàn)檫@種結(jié)論本身是一個在什么場景下適合使用多線程提升效率的問題弛作。
一般來講涕蜂,線程有效執(zhí)行時間超過線程切換的時間時候基本就適合使用多線程進(jìn)行并行處理。
跟CountDownLatch在這里做一個簡單對比
一映琳、昨天分析的CountDownLatch它是一個用于倒計數(shù)控制的机隙,它側(cè)重在不同任務(wù)的并行處理,(生產(chǎn)門子萨西,生產(chǎn)輪胎有鹿,生產(chǎn)把手)最后組裝成車而CyclicBarrier往往更多用在并行的相同任務(wù)的處理。
二谎脯、CountDownLatch是一次性的葱跋,而CyclicBarrier是可循環(huán)使用的,這里面有個小細(xì)節(jié),CyclicBarrier其實(shí)是通過每個子線程去撞柵欄完成的generation的改變(這部分源碼明天再補(bǔ)充)娱俺。所以會要求分線程執(zhí)行任務(wù)比較對齊稍味。這也跟上一點(diǎn)相呼應(yīng)。
三荠卷、CyclicBarrier每次換代時候都會調(diào)用在初始化CyclicBarrier 傳入的Runnable類型的參數(shù)模庐,也就是源碼里面的command參數(shù)。有點(diǎn)Map reduce的趕腳哈僵朗。這種撞柵欄的感覺有點(diǎn)JVM在做GC時候的saftPoint(安全點(diǎn))的趕腳赖欣,不展開了有興趣的哥們可以期待小弟的JVM相關(guān)文檔,慢慢都會寫一些
時間不早洗洗睡各位有不同意見還請多多留言验庙,共同進(jìn)步
源碼分析待補(bǔ)充