1.CyclicBarrier
CyclicBarrier初始化時(shí)規(guī)定一個(gè)數(shù)目,然后計(jì)算調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù)。當(dāng)線程數(shù)達(dá)到了這個(gè)數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù)。
功能:可以使一定數(shù)量的參與方反復(fù)在指定的地方(就是調(diào)用await()的地方)匯集(阻塞自己) ,只有所有參與方都調(diào)用了await(),柵欄就會(huì)打開(kāi),所有線程阻塞才會(huì)解除
適用場(chǎng)景:CyclicBarrier可以用在所有子線程之間互相等待多次的情形。如并行迭代算法,這種算法通常將一個(gè)問(wèn)題拆分成一系列相互獨(dú)立的子問(wèn)題
code
public static void main(String[] args){ //CyclicBarrier 是幾個(gè)線程導(dǎo)了一個(gè)點(diǎn)后等其他線程到了一起出發(fā) 哪個(gè)地方等就在哪個(gè)地方調(diào)用await()
ExecutorService threadpool=Executors.newCachedThreadPool();
final CyclicBarrier cb=new CyclicBarrier(3);
for(int i=0;i<3;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long)Math.random()*10000);
System.out.println("線程" + Thread.currentThread().getName() + "即將到達(dá)地點(diǎn)1,當(dāng)前已有"+(cb.getNumberWaiting()+1 )+"個(gè)線程");
//到達(dá)出發(fā)點(diǎn)
cb.await();
Thread.sleep((long)Math.random()*10000);
System.out.println("線程"+Thread.currentThread().getName()+"即將到達(dá)集合地點(diǎn)2,當(dāng)前已有"+(cb.getNumberWaiting()+1)+"個(gè)線程");
cb.await();
Thread.sleep((long)Math.random()*10000);
System.out.println("線程"+Thread.currentThread().getName()+"即將到達(dá)集合地點(diǎn)3,當(dāng)前已有"+(cb.getNumberWaiting()+1)+"個(gè)線程");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
threadpool.execute(runnable);
}
}
2.CountDownLatch(閉鎖)
CountDownLatch先初始化規(guī)定一個(gè)計(jì)數(shù)器,沒(méi)調(diào)用一次countDwon()計(jì)數(shù)器減一,如果數(shù)量變成0就達(dá)到結(jié)束狀態(tài),閉鎖打開(kāi)(await()方法阻塞解除)
閉鎖的作用相當(dāng)于一扇門(mén),閉鎖的狀態(tài)沒(méi)有結(jié)束時(shí)這扇門(mén)一直視關(guān)閉的,閉鎖達(dá)到結(jié)束狀態(tài)時(shí)將允許所有線程通過(guò)
適用場(chǎng)景:CountDownLatch可以應(yīng)用于主線程等待所有子線程結(jié)束后再繼續(xù)執(zhí)行的情況。閉鎖可以用來(lái)確定某些活動(dòng)直到其他活動(dòng)都完成了才繼續(xù)執(zhí)行
如確保某個(gè)服務(wù)在其他服務(wù)啟動(dòng)之后才能啟動(dòng);在多玩家的游戲中確保所有玩家就緒后才執(zhí)行
Code
public static void main(String[] args){
ExecutorService threadpool = Executors.newCachedThreadPool();
final CountDownLatch cdorder=new CountDownLatch(1);
final CountDownLatch cdanswer=new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
System.out.println("線程"+Thread.currentThread().getName()+"正準(zhǔn)備接受命令");
cdorder.await();
System.out.println("線程"+Thread.currentThread().getName()+"已接受命令");
Thread.sleep((long)Math.random()*10000);
System.out.println("線程"+Thread.currentThread().getName()+"回應(yīng)命令處理結(jié)果");
cdanswer.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
threadpool.execute(runnable);
}
try {
Thread.sleep((long)Math.random()*10000);
System.out.println("線程"+Thread.currentThread().getName()+"即將發(fā)布命令");
cdorder.countDown();
System.out.println("線程"+Thread.currentThread().getName()+"已發(fā)送命令,正在等待結(jié)果");
cdanswer.await();
System.out.println("線程"+Thread.currentThread().getName()+"已收到所有響應(yīng)結(jié)果");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
threadpool.shutdown();
}
CountDownLatch與CyclicBarrier區(qū)別
1.CountDownLatch減計(jì)數(shù)方式
CyclicBarrier 加計(jì)數(shù)方式
2.CountDownLatch計(jì)算為0時(shí)釋放所有等待的線程
CyclicBarrier 計(jì)數(shù)達(dá)到指定值時(shí)釋放所有等待線程
3.CountDownLatch計(jì)數(shù)為0時(shí),無(wú)法重置
CyclicBarrier 計(jì)數(shù)達(dá)到指定值時(shí),計(jì)數(shù)置為0重新開(kāi)始
4.CountDownLatch調(diào)用countDown()方法計(jì)數(shù)減一,調(diào)用await()方法只進(jìn)行阻塞,對(duì)計(jì)數(shù)沒(méi)任何影響
CyclicBarrier 調(diào)用await()方法計(jì)數(shù)加1,若加1 的值不等于構(gòu)造方法的值,則線程阻塞
5.CountDownLatch不可重復(fù)利用
CyclicBarrier可重復(fù)利用
6.線程在countDown()之后,會(huì)繼續(xù)執(zhí)行自己的任務(wù),而CyclicBarrier會(huì)在所有線程任務(wù)結(jié)束之后,才會(huì)進(jìn)行后續(xù)任務(wù)
3.Semaphore(信號(hào)量)
計(jì)數(shù)信號(hào)量用來(lái)控制同時(shí)訪問(wèn)某個(gè)特定資源的操作數(shù)量,信號(hào)量維護(hù)了一個(gè)許可集。如有必要,在許可可用前會(huì)阻塞每一個(gè) acquire(),然后再獲取該許可。每個(gè) release() 添加一個(gè)許可,從而可能釋放一個(gè)正在阻塞的獲取者。但是,不使用實(shí)際的許可對(duì)象,Semaphore 只對(duì)可用許可的號(hào)碼進(jìn)行計(jì)數(shù),并采取相應(yīng)的行動(dòng)。拿到信號(hào)量的線程可以進(jìn)入代碼,否則就等待。通過(guò)acquire()和release()獲取和釋放訪問(wèn)許可。初始值為1的Semaphore二值信號(hào)量可以用作互斥體
適用場(chǎng)景:Semaphore可以用于做流量控制,特別公用資源有限的應(yīng)用場(chǎng)景,比如數(shù)據(jù)庫(kù)連接。假如有一個(gè)需求,要讀取幾萬(wàn)個(gè)文件的數(shù)據(jù),因?yàn)槎际荌O密集型任務(wù),我們可以啟動(dòng)幾十個(gè)線程并發(fā)的讀取,但是如果讀到內(nèi)存后,還需要存儲(chǔ)到數(shù)據(jù)庫(kù)中,而數(shù)據(jù)庫(kù)的連接數(shù)只有10個(gè),這時(shí)我們必須控制只有十個(gè)線程同時(shí)獲取數(shù)據(jù)庫(kù)連接保存數(shù)據(jù),否則會(huì)報(bào)錯(cuò)無(wú)法獲取數(shù)據(jù)庫(kù)連接。這個(gè)時(shí)候,我們就可以使用Semaphore來(lái)做流控
Code
public static void main(String[] args){
ExecutorService threadpool = Executors.newCachedThreadPool();
final Semaphore sp=new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
sp.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +" 進(jìn)入,當(dāng)前有" + (3- sp.availablePermits())+ " 個(gè)線程并發(fā)");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程"+Thread.currentThread().getName()+"即將離開(kāi)");
sp.release();
System.out.println("線程"+Thread.currentThread().getName()+"已離開(kāi),當(dāng)前已有"+(3-sp.availablePermits())+"個(gè)線程并發(fā)");
}
};
threadpool.execute(runnable);
}
}
控制任務(wù)提交速度
class BoundedExecutor{//使用Semaphore控制任務(wù)的提交速率
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec,Semaphore semaphore){
this.exec=exec;
this.semaphore=semaphore;
}
public void submitTask(final Runnable command) throws InterruptedException{
semaphore.acquire();
try{
exec.execute(new Runnable() {
@Override
public void run() {
try{
command.run();
}finally{
semaphore.release();
}
}
});
}catch(RejectedExecutionException e){
semaphore.release();
}
}
}