java.util.concurrent包(5)——CountDownLatch使用

Java的concurrent包裏面的CountDownLatch其實可以被看作一個計數器,只不過這個計數器的操作是原子操作,同時只能有一個線程去操作這個計數器,也就是同時只能有一個線程去減這個計數器裏面的值。

可以向CountDownLatch對象設置一個初始的數字作爲計數值,任何調用這個對象上的await()方法都會阻塞,直到這個計數器的計數值被其他的線程減爲0爲止。

CountDownLatch的一個非常典型的應用場景是:有一個任務想要往下執行,但必須要等到其他的任務執行完畢後纔可以繼續往下執行。假如這個想要繼續往下執行的任務調用一個CountDownLatch對象的await()方法,其他的任務執行完自己的任務後調用同一個CountDownLatch對象上的countDown()方法,這個調用await()方法的任務將一直阻塞等待,直到這個CountDownLatch對象的計數值減到0爲止。

主要方法
public CountDownLatch(int count)
public void countDown()
public void await() throws InterruptedException
 
構造方法參數指定了計數的次數
countDown方法,當前線程調用此方法,則計數減一
awaint方法,調用此方法會一直阻塞當前線程,直到計時器的值爲0

例如有三個工人在爲老闆幹活,這個老闆有一個習慣,就是當三個工人把一天的活都幹完了的時候,他就來檢查所有工人所幹的活。記住這個條件:三個工人先全部幹完活,老闆才檢查。

public class Boss implements Runnable
{
private final CountDownLatch countDownLatch;
private String name;

public Boss(String name, CountDownLatch countDownLatch)
{
super();
this.name = name;
this.countDownLatch = countDownLatch;
}

public void run()
{
try
{
System.out.println("老闆" + name + "正在等工人把活幹完");
countDownLatch.await();
System.out.println("工人活都幹完了,老闆開始檢查了!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

public class Worker implements Runnable
{
private final CountDownLatch countDownLatch;
private String name;

public Worker(String name, CountDownLatch countDownLatch)
{
super();
this.name = name;
this.countDownLatch = countDownLatch;
}

public void run()
{
doWork();
countDownLatch.countDown();
}

private void doWork()
{
try
{
System.out.println(this.name + "正在幹活!");
long duration = new Random().nextInt(5) * 1000;
Thread.sleep(duration);
System.out.println(this.name + "活幹完啦!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

public class BossWorkerTest
{
public static void main(String[] args)
{
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(3);

Worker w1 = new Worker("張一", countDownLatch);
Worker w2 = new Worker("張二", countDownLatch);
Worker w3 = new Worker("張三", countDownLatch);
Boss boss = new Boss("王老闆", countDownLatch);
executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss);
executor.shutdown();
}
}

下面是本地機器上運行的一次結果,每次運行的結果可能與下面不一樣,但老闆檢查永遠是在最後面。

張三正在幹活!
張二正在幹活!
張一正在幹活!
張三活幹完啦!
老闆王老闆正在等工人把活幹完
張一活幹完啦!
張二活幹完啦!
工人活都幹完了,老闆開始檢查了!

再看一個例子,該程序用來模擬發送命令與執行命令。
主線程代表教練,新建3個線程代表運動員。
運動員一直等待着教練下達命令。若教練沒有下達命令,則運動員們都必須等待。
一旦命令下達,運動員們都去執行自己的任務,教練處於等待狀態,運動員們任務執行完畢則報告給教練,教練則結束等待。

public class CountdownLatchTest
{
public static void main(String[] args)
{
// 創建一個線程池
ExecutorService service = Executors.newCachedThreadPool();
// 教練的命令設置爲1,教練一下達命令,則countDown變爲0,運動員們執行任務
final CountDownLatch cdOrder = new CountDownLatch(1);
// 因爲有三個運動員故初始值爲3,每個運動員執行任務完畢則countDown一次

// 當三個都執行完畢變爲0則教練停止等待
final CountDownLatch cdAnswer = new CountDownLatch(3);
for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
public void run()
{
try
{
System.out.println("線程" + Thread.currentThread().getName() + "正準備接受命令");
// 運動員們都處於等待命令狀態
cdOrder.await();
System.out.println("線程" + Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("線程" + Thread.currentThread().getName() + "迴應命令處理結果");
// 任務執行完畢返回給教練,cdAnswer減1
cdAnswer.countDown();
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try
{
Thread.sleep((long) (Math.random() * 10000));

System.out.println("線程" + Thread.currentThread().getName() + "即將發佈命令");
// 發送命令cdOrder減1,處於等待的運動員們停止等待轉去執行任務
cdOrder.countDown();
System.out.println("線程" + Thread.currentThread().getName() + "已發送命令,正在等待結果");
// 命令發送後教練處於等待狀態,一旦cdAnswer爲0時停止等待繼續往下執行
cdAnswer.await();
System.out.println("線程" + Thread.currentThread().getName() + "已收到所有響應結果");
}
catch (Exception e)
{
e.printStackTrace();
}
service.shutdown();
}
}

原帖地址:
http://zapldy.iteye.com/blog/746458
http://www.cnblogs.com/liuling/p/2013-8-20-02.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章