這是java高併發系列第17篇。
本文主要內容:
- 介紹CyclicBarrier
- 6個示例介紹CyclicBarrier的使用
- 對比CyclicBarrier和CountDownLatch
CyclicBarrier簡介
CyclicBarrier通常稱為迴圈屏障。它和CountDownLatch很相似,都可以使執行緒先等待然後再執行。不過CountDownLatch是使一批執行緒等待另一批執行緒執行完後再執行;而CyclicBarrier只是使等待的執行緒達到一定數目後再讓它們繼續執行。故而CyclicBarrier內部也有一個計數器,計數器的初始值在建立物件時通過構造引數指定,如下所示:
public CyclicBarrier(int parties) {
this(parties, null);
}
每呼叫一次await()方法都將使阻塞的執行緒數+1,只有阻塞的執行緒數達到設定值時屏障才會開啟,允許阻塞的所有執行緒繼續執行。除此之外,CyclicBarrier還有幾點需要注意的地方:
- CyclicBarrier的計數器可以重置而CountDownLatch不行,這意味著CyclicBarrier例項可以被重複使用而CountDownLatch只能被使用一次。而這也是迴圈屏障迴圈二字的語義所在。
- CyclicBarrier允許使用者自定義barrierAction操作,這是個可選操作,可以在建立CyclicBarrier物件時指定
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
一旦使用者在建立CyclicBarrier物件時設定了barrierAction引數,則在阻塞執行緒數達到設定值屏障開啟前,會呼叫barrierAction的run()方法完成使用者自定義的操作。
示例1:簡單使用CyclicBarrier
公司組織旅遊,大家都有經歷過,10個人,中午到飯點了,需要等到10個人都到了才能開飯,先到的人坐那等著,程式碼如下:
package com.itsoku.chat15;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo1 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
@Override
public void run() {
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
long starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
cyclicBarrier.await();
long endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
new T("員工" + i, i).start();
}
}
}
輸出:
員工1,sleep:1 等待了9000(ms),開始吃飯了!
員工9,sleep:9 等待了1000(ms),開始吃飯了!
員工8,sleep:8 等待了2001(ms),開始吃飯了!
員工7,sleep:7 等待了3001(ms),開始吃飯了!
員工6,sleep:6 等待了4001(ms),開始吃飯了!
員工4,sleep:4 等待了6000(ms),開始吃飯了!
員工5,sleep:5 等待了5000(ms),開始吃飯了!
員工10,sleep:10 等待了0(ms),開始吃飯了!
員工2,sleep:2 等待了7999(ms),開始吃飯了!
員工3,sleep:3 等待了7000(ms),開始吃飯了!
程式碼中模擬了10個員工上桌吃飯的場景,等待所有員工都到齊了才能開發,可以看到第10個員工最慢,前面的都在等待第10個員工,員工1等待了9秒,上面程式碼中呼叫cyclicBarrier.await();
會讓當前執行緒等待。當10個員工都呼叫了cyclicBarrier.await();
之後,所有處於等待中的員工都會被喚醒,然後繼續執行。
示例2:重複使用CyclicBarrier
對示例1進行改造一下,吃飯完畢之後,所有人都去車上,待所有人都到車上之後,驅車去下一景點玩。
package com.itsoku.chat15;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo2 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
//等待吃飯
void eat() {
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
long starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
cyclicBarrier.await();
long endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
//休眠sleep時間,模擬當前員工吃飯耗時
TimeUnit.SECONDS.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
//等待所有人到齊之後,開車去下一站
void drive() {
try {
long starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
cyclicBarrier.await();
long endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),去下一景點的路上!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//等待所有人到齊之後吃飯,先到的人坐那等著,什麼事情不要幹
this.eat();
//等待所有人到齊之後開車去下一景點,先到的人坐那等著,什麼事情不要幹
this.drive();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
new T("員工" + i, i).start();
}
}
}
輸出:
員工10,sleep:10 等待了0(ms),開始吃飯了!
員工5,sleep:5 等待了5000(ms),開始吃飯了!
員工6,sleep:6 等待了4000(ms),開始吃飯了!
員工9,sleep:9 等待了1001(ms),開始吃飯了!
員工4,sleep:4 等待了6000(ms),開始吃飯了!
員工3,sleep:3 等待了7000(ms),開始吃飯了!
員工1,sleep:1 等待了9001(ms),開始吃飯了!
員工2,sleep:2 等待了8000(ms),開始吃飯了!
員工8,sleep:8 等待了2001(ms),開始吃飯了!
員工7,sleep:7 等待了3000(ms),開始吃飯了!
員工10,sleep:10 等待了0(ms),去下一景點的路上!
員工1,sleep:1 等待了8998(ms),去下一景點的路上!
員工5,sleep:5 等待了4999(ms),去下一景點的路上!
員工4,sleep:4 等待了5999(ms),去下一景點的路上!
員工3,sleep:3 等待了6998(ms),去下一景點的路上!
員工2,sleep:2 等待了7998(ms),去下一景點的路上!
員工9,sleep:9 等待了999(ms),去下一景點的路上!
員工8,sleep:8 等待了1999(ms),去下一景點的路上!
員工7,sleep:7 等待了2999(ms),去下一景點的路上!
員工6,sleep:6 等待了3999(ms),去下一景點的路上!
坑,又是員工10最慢,要提升效率了,不能吃的太多,得減肥。
程式碼中CyclicBarrier相當於使用了2次,第一次用於等待所有人到達後開飯,第二次用於等待所有人上車後驅車去下一景點。注意一些先到的員工會在其他人到達之前,都處於等待狀態(cyclicBarrier.await();
會讓當前執行緒阻塞),無法幹其他事情,等到最後一個人到了會喚醒所有人,然後繼續。
CyclicBarrier內部相當於有個計數器(構造方法傳入的),每次呼叫
await();
後,計數器會減1,並且await()方法會讓當前執行緒阻塞,等待計數器減為0的時候,所有在await()上等待的執行緒被喚醒,然後繼續向下執行,此時計數器又會被還原為建立時的值,然後可以繼續再次使用。
示例3:最後到的人給大家上酒,然後開飯
還是示例1中的例子,員工10是最後到達的,讓所有人都久等了,那怎麼辦,得給所有人倒酒,然後開飯,程式碼如下:
package com.itsoku.chat15;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo3 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
//模擬倒酒,花了2秒,又得讓其他9個人等2秒
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "說,不好意思,讓大家久等了,給大家倒酒賠罪!");
});
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
@Override
public void run() {
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
long starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
cyclicBarrier.await();
long endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
new T("員工" + i, i).start();
}
}
}
輸出:
員工10說,不好意思,讓大家久等了,給大家倒酒賠罪!
員工10,sleep:10 等待了2000(ms),開始吃飯了!
員工1,sleep:1 等待了11000(ms),開始吃飯了!
員工2,sleep:2 等待了10000(ms),開始吃飯了!
員工5,sleep:5 等待了7000(ms),開始吃飯了!
員工7,sleep:7 等待了5000(ms),開始吃飯了!
員工9,sleep:9 等待了3000(ms),開始吃飯了!
員工4,sleep:4 等待了8000(ms),開始吃飯了!
員工3,sleep:3 等待了9001(ms),開始吃飯了!
員工8,sleep:8 等待了4001(ms),開始吃飯了!
員工6,sleep:6 等待了6001(ms),開始吃飯了!
程式碼中建立CyclicBarrier
物件時,多傳入了一個引數(內部是倒酒操作),先到的人先等待,待所有人都到齊之後,需要先給大家倒酒,然後喚醒所有等待中的人讓大家開飯。從輸出結果中我們發現,倒酒操作是由最後一個人操作的,最後一個人倒酒完畢之後,才喚醒所有等待中的其他員工,讓大家開飯。
示例4:其中一個人等待中被打斷了
員工5等待中,突然接了個電話,有點急事,然後就拿起筷子開吃了,其他人會怎麼樣呢?看著他吃麼?
程式碼如下:
package com.itsoku.chat15;
import java.sql.Time;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo4 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
@Override
public void run() {
long starTime = 0, endTime = 0;
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
System.out.println(this.getName() + "到了!");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
int sleep = 0;
if (i == 10) {
sleep = 10;
}
T t = new T("員工" + i, sleep);
t.start();
if (i == 5) {
//模擬員工5接了個電話,將自己等待吃飯給打斷了
TimeUnit.SECONDS.sleep(1);
System.out.println(t.getName() + ",有點急事,我先開幹了!");
t.interrupt();
TimeUnit.SECONDS.sleep(2);
}
}
}
}
輸出:
員工4到了!
員工3到了!
員工5到了!
員工1到了!
員工2到了!
員工5,有點急事,我先開幹了!
java.util.concurrent.BrokenBarrierException
員工1,sleep:0 等待了1001(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
員工3,sleep:0 等待了1001(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
員工4,sleep:0 等待了1001(ms),開始吃飯了!
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
員工2,sleep:0 等待了1001(ms),開始吃飯了!
員工5,sleep:0 等待了1002(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.util.concurrent.BrokenBarrierException
員工6到了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
員工9到了!
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
員工8到了!
員工7到了!
員工6,sleep:0 等待了0(ms),開始吃飯了!
員工7,sleep:0 等待了1(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
員工8,sleep:0 等待了1(ms),開始吃飯了!
員工9,sleep:0 等待了1(ms),開始吃飯了!
Disconnected from the target VM, address: '127.0.0.1:64413', transport: 'socket'
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
員工10到了!
員工10,sleep:10 等待了0(ms),開始吃飯了!
輸出的資訊看著有點亂,給大家理一理,員工5遇到急事,拿起筷子就是吃,這樣好麼,當然不好,他這麼做了,後面看他這麼做了都跟著這麼做(這種場景是不是很熟悉,有一個人拿起筷子先吃起來,其他人都跟著上了),直接不等其他人了,拿起筷子就開吃了。CyclicBarrier遇到這種情況就是這麼處理的。前面4個員工都在await()
處等待著,員工5也在await()
上等待著,等了1秒(TimeUnit.SECONDS.sleep(1);
),接了個電話,然後給員工5傳送中斷訊號後(t.interrupt();
),員工5的await()方法會觸發InterruptedException
異常,此時其他等待中的前4個員工,看著5開吃了,自己立即也不等了,內部從await()
方法中觸發BrokenBarrierException
異常,然後也開吃了,後面的6/7/8/9/10員工來了以後發現大家都開吃了,自己也不等了,6-10員工呼叫await()
直接丟擲了BrokenBarrierException
異常,然後繼續向下。
結論:
- 內部有一個人把規則破壞了(接收到中斷訊號),其他人都不按規則來了,不會等待了
- 接收到中斷訊號的執行緒,await方法會觸發InterruptedException異常,然後被喚醒向下執行
- 其他等待中 或者後面到達的執行緒,會在await()方法上觸發
BrokenBarrierException
異常,然後繼續執行
示例5:其中一個人只願意等待5秒
基於示例1,員工1只願意等的5秒,5s後如果大家還沒到期,自己要開吃了,員工1開吃了,其他人會怎麼樣呢?
package com.itsoku.chat15;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo5 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
@Override
public void run() {
long starTime = 0, endTime = 0;
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
System.out.println(this.getName() + "到了!");
if (this.getName().equals("員工1")) {
cyclicBarrier.await(5, TimeUnit.SECONDS);
} else {
cyclicBarrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
T t = new T("員工" + i, i);
t.start();
}
}
}
輸出:
員工1到了!
員工2到了!
員工3到了!
員工4到了!
員工5到了!
員工6到了!
員工1,sleep:1 等待了5001(ms),開始吃飯了!
員工5,sleep:5 等待了1001(ms),開始吃飯了!
java.util.concurrent.TimeoutException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:32)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
員工6,sleep:6 等待了2(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
員工2,sleep:2 等待了4002(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
員工3,sleep:3 等待了3001(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
員工4,sleep:4 等待了2001(ms),開始吃飯了!
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
java.util.concurrent.BrokenBarrierException
員工7到了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
員工7,sleep:7 等待了0(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
員工8到了!
員工8,sleep:8 等待了0(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
員工9到了!
java.util.concurrent.BrokenBarrierException
員工9,sleep:9 等待了0(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
java.util.concurrent.BrokenBarrierException
員工10到了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
員工10,sleep:10 等待了0(ms),開始吃飯了!
at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
從輸出結果中我們可以看到:1等待5秒之後,開吃了,其他等待人都開吃了,後面來的人不等待,直接開吃了。
員工1呼叫有參await
方法等待5秒之後,觸發了TimeoutException
異常,然後繼續向下執行,其他的在5開吃之前已經等了一會的的幾個員工,他們看到5開吃了,自己立即不等待了,也開吃了(他們的await
丟擲了BrokenBarrierException
異常);還有幾個員工在5開吃之後到達的,他們直接不等待了,直接丟擲BrokenBarrierException
異常,然後也開吃了。
結論:
等待超時的方法
public int await(long timeout, TimeUnit unit) throws InterruptedException,BrokenBarrierException,TimeoutException
- 內部有一個人把規則破壞了(等待超時),其他人都不按規則來了,不會等待了
- 等待超時的執行緒,await方法會觸發TimeoutException異常,然後被喚醒向下執行
其他等待中或者後面到達的執行緒,會在await()方法上觸發
BrokenBarrierException
異常,然後繼續執行
示例6:重建規則
示例5中改造一下,員工1等待5秒超時之後,開吃了,打破了規則,先前等待中的以及後面到達的都不按規則來了,都拿起筷子開吃。過了一會,導遊重新告知大家,要按規則來,然後重建了規則,大家都按規則來了。
程式碼如下:
package com.itsoku.chat15;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 微信公眾號:javacode2018,獲取年薪50萬java課程
*/
public class Demo6 {
public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
//規則是否已重建
public static boolean guizhe = false;
public static class T extends Thread {
int sleep;
public T(String name, int sleep) {
super(name);
this.sleep = sleep;
}
@Override
public void run() {
long starTime = 0, endTime = 0;
try {
//模擬休眠
TimeUnit.SECONDS.sleep(sleep);
starTime = System.currentTimeMillis();
//呼叫await()的時候,當前執行緒將會被阻塞,需要等待其他員工都到達await了才能繼續
System.out.println(this.getName() + "到了!");
if (!guizhe) {
if (this.getName().equals("員工1")) {
cyclicBarrier.await(5, TimeUnit.SECONDS);
} else {
cyclicBarrier.await();
}
} else {
cyclicBarrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
endTime = System.currentTimeMillis();
System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),開始吃飯了!");
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
T t = new T("員工" + i, i);
t.start();
}
//等待10秒之後,重置,重建規則
TimeUnit.SECONDS.sleep(15);
cyclicBarrier.reset();
guizhe = true;
System.out.println("---------------大家太皮了,請大家按規則來------------------");
//再來一次
for (int i = 1; i <= 10; i++) {
T t = new T("員工" + i, i);
t.start();
}
}
}
輸出:
員工1到了!
員工2到了!
員工3到了!
員工4到了!
員工5到了!
java.util.concurrent.TimeoutException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:36)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
員工6到了!
員工1,sleep:1 等待了5002(ms),開始吃飯了!
員工6,sleep:6 等待了4(ms),開始吃飯了!
員工4,sleep:4 等待了2004(ms),開始吃飯了!
員工5,sleep:5 等待了1004(ms),開始吃飯了!
員工3,sleep:3 等待了3002(ms),開始吃飯了!
員工2,sleep:2 等待了4004(ms),開始吃飯了!
員工7到了!
員工7,sleep:7 等待了0(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
員工8到了!
員工8,sleep:8 等待了0(ms),開始吃飯了!
java.util.concurrent.BrokenBarrierException
員工9到了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
員工9,sleep:9 等待了0(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
java.util.concurrent.BrokenBarrierException
員工10到了!
at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
員工10,sleep:10 等待了0(ms),開始吃飯了!
at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
---------------大家太皮了,請大家按規則來------------------
員工1到了!
員工2到了!
員工3到了!
員工4到了!
員工5到了!
員工6到了!
員工7到了!
員工8到了!
員工9到了!
員工10到了!
員工10,sleep:10 等待了0(ms),開始吃飯了!
員工1,sleep:1 等待了9000(ms),開始吃飯了!
員工2,sleep:2 等待了8000(ms),開始吃飯了!
員工3,sleep:3 等待了6999(ms),開始吃飯了!
員工7,sleep:7 等待了3000(ms),開始吃飯了!
員工6,sleep:6 等待了4000(ms),開始吃飯了!
員工5,sleep:5 等待了5000(ms),開始吃飯了!
員工4,sleep:4 等待了6000(ms),開始吃飯了!
員工9,sleep:9 等待了999(ms),開始吃飯了!
員工8,sleep:8 等待了1999(ms),開始吃飯了!
第一次規則被打亂了,過了一會導遊重建了規則(cyclicBarrier.reset();
),接著又重來來了一次模擬等待吃飯的操作,正常了。
CountDownLatch和CyclicBarrier的區別
還是舉例子說明一下:
CountDownLatch示例
主管相當於 CountDownLatch,幹活的小弟相當於做事情的執行緒。
老闆交給主管了一個任務,讓主管搞完之後立即上報給老闆。主管下面有10個小弟,接到任務之後將任務劃分為10個小任務分給每個小弟去幹,主管一直處於等待狀態(主管會呼叫await()
方法,此方法會阻塞當前執行緒),讓每個小弟幹完之後通知一下主管(呼叫countDown()
方法通知主管,此方法會立即返回),主管等到所有的小弟都做完了,會被喚醒,從await()方法上甦醒,然後將結果反饋給老闆。期間主管會等待,會等待所有小弟將結果彙報給自己。
而CyclicBarrier是一批執行緒讓自己等待,等待所有的執行緒都準備好了,自己才能繼續。
java高併發系列
- java高併發系列 - 第1天:必須知道的幾個概念
- java高併發系列 - 第2天:併發級別
- java高併發系列 - 第3天:有關並行的兩個重要定律
- java高併發系列 - 第4天:JMM相關的一些概念
- java高併發系列 - 第5天:深入理解程式和執行緒
- java高併發系列 - 第6天:執行緒的基本操作
- java高併發系列 - 第7天:volatile與Java記憶體模型
- java高併發系列 - 第8天:執行緒組
- java高併發系列 - 第9天:使用者執行緒和守護執行緒
- java高併發系列 - 第10天:執行緒安全和synchronized關鍵字
- java高併發系列 - 第11天:執行緒中斷的幾種方式
- java高併發系列 - 第12天JUC:ReentrantLock重入鎖
- java高併發系列 - 第13天:JUC中的Condition物件
- java高併發系列 - 第14天:JUC中的LockSupport工具類,必備技能
- java高併發系列 - 第15天:JUC中的Semaphore(訊號量)
- java高併發系列 - 第16天:JUC中等待多執行緒完成的工具類CountDownLatch,必備技能
高併發系列連載中,感興趣的加我微信itsoku,一起交流,關注公眾號:路人甲Java,每天獲取最新文章,送年薪50萬java學習路線視訊!