版權(quán)聲明:本文為小斑馬學(xué)習(xí)總結(jié)文章慎皱,技術(shù)來(lái)源于韋東山著作,轉(zhuǎn)載請(qǐng)注明出處叶骨!
在java 1.5中宝冕,提供了一些非常有用的輔助類來(lái)幫助我們進(jìn)行并發(fā)編程,比如CountDownLatch邓萨,CyclicBarrier和Semaphore地梨,今天我們就來(lái)學(xué)習(xí)一下這三個(gè)輔助類的用法。
一缔恳、CountDownLatch用法
CountDownLatch類位于java.util.concurrent包下宝剖,利用它可以實(shí)現(xiàn)類似計(jì)數(shù)器的功能。比如有一個(gè)任務(wù)A歉甚,它要等待其他4個(gè)任務(wù)執(zhí)行完畢之后才能執(zhí)行万细,此時(shí)就可以利用CountDownLatch來(lái)實(shí)現(xiàn)這種功能了。
CountDownLatch類只提供了一個(gè)構(gòu)造器:
public CountDownLatch(int count) { }; //參數(shù)count為計(jì)數(shù)值
然后下面這3個(gè)方法是CountDownLatch類中最重要的方法:
public void await() throws InterruptedException { }; //調(diào)用await()方法的線程會(huì)被掛起纸泄,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行
public boolean await(long timeout, TimeUnit unit) throws
InterruptedException { }; //和await()類似赖钞,只不過(guò)等待一定的時(shí)間后
count值還沒(méi)變?yōu)?的話就會(huì)繼續(xù)執(zhí)行
public void countDown() { }; //將count值減1
下面看一個(gè)例子大家就清楚CountDownLatch的用法了:
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執(zhí)行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執(zhí)行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2個(gè)子線程執(zhí)行完畢...");
latch.await();
System.out.println("2個(gè)子線程已經(jīng)執(zhí)行完畢");
System.out.println("繼續(xù)執(zhí)行主線程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
執(zhí)行結(jié)果:
線程Thread-0正在執(zhí)行
線程Thread-1正在執(zhí)行
等待2個(gè)子線程執(zhí)行完畢...
線程Thread-0執(zhí)行完畢
線程Thread-1執(zhí)行完畢
2個(gè)子線程已經(jīng)執(zhí)行完畢
繼續(xù)執(zhí)行主線程
二、CyclicBarrier用法
字面意思回環(huán)柵欄聘裁,通過(guò)它可以實(shí)現(xiàn)讓一組線程等待至某個(gè)狀態(tài)之后再全部同時(shí)執(zhí)行雪营。叫做回環(huán)是因?yàn)楫?dāng)所有等待線程都被釋放以后,CyclicBarrier可以被重用衡便。我們暫且把這個(gè)狀態(tài)就叫做barrier献起,當(dāng)調(diào)用await()方法之后洋访,線程就處于barrier了。
CyclicBarrier類位于java.util.concurrent包下谴餐,CyclicBarrier提供2個(gè)構(gòu)造器:
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
參數(shù)parties指讓多少個(gè)線程或者任務(wù)等待至barrier狀態(tài)姻政;參數(shù)barrierAction為當(dāng)這些線程都達(dá)到barrier狀態(tài)時(shí)會(huì)執(zhí)行的內(nèi)容。
然后CyclicBarrier中最重要的方法就是await方法岂嗓,它有2個(gè)重載版本:
public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
第一個(gè)版本比較常用汁展,用來(lái)掛起當(dāng)前線程,直至所有線程都到達(dá)barrier狀態(tài)再同時(shí)執(zhí)行后續(xù)任務(wù)厌殉;
第二個(gè)版本是讓這些線程等待至一定的時(shí)間善镰,如果還有線程沒(méi)有到達(dá)barrier狀態(tài)就直接讓到達(dá)barrier的線程執(zhí)行后續(xù)任務(wù)。
下面舉幾個(gè)例子就明白了:
假若有若干個(gè)線程都要進(jìn)行寫數(shù)據(jù)操作年枕,并且只有所有線程都完成寫數(shù)據(jù)操作之后炫欺,這些線程才能繼續(xù)做后面的事情,此時(shí)就可以利用CyclicBarrier了:
public class Test {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
try {
Thread.sleep(5000); //以睡眠來(lái)模擬寫入數(shù)據(jù)操作
System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢熏兄,等待其他線程寫入完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有線程寫入完畢品洛,繼續(xù)處理其他任務(wù)...");
}
}
從上面輸出結(jié)果可以看出,每個(gè)寫入線程執(zhí)行完寫數(shù)據(jù)操作之后摩桶,就在等待其他線程寫入操作完畢桥状。
當(dāng)所有線程線程寫入操作完畢之后,所有線程就繼續(xù)進(jìn)行后續(xù)的操作了硝清。
如果說(shuō)想在所有線程寫入操作完之后辅斟,進(jìn)行額外的其他操作可以為CyclicBarrier提供Runnable參數(shù):
public class Test {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N,new Runnable() {
@Override
public void run() {
System.out.println("當(dāng)前線程"+Thread.currentThread().getName());
}
});
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
try {
Thread.sleep(5000); //以睡眠來(lái)模擬寫入數(shù)據(jù)操作
System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢,等待其他線程寫入完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有線程寫入完畢芦拿,繼續(xù)處理其他任務(wù)...");
}
}
運(yùn)行結(jié)果:
線程Thread-0正在寫入數(shù)據(jù)...
線程Thread-1正在寫入數(shù)據(jù)...
線程Thread-2正在寫入數(shù)據(jù)...
線程Thread-3正在寫入數(shù)據(jù)...
線程Thread-0寫入數(shù)據(jù)完畢士飒,等待其他線程寫入完畢
線程Thread-1寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-2寫入數(shù)據(jù)完畢蔗崎,等待其他線程寫入完畢
線程Thread-3寫入數(shù)據(jù)完畢酵幕,等待其他線程寫入完畢
當(dāng)前線程Thread-3
所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢缓苛,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢芳撒,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢,繼續(xù)處理其他任務(wù)...