1. 程式人生 > >執行緒同步工具(三)等待多個併發事件完成

執行緒同步工具(三)等待多個併發事件完成

宣告:本文是《 Java 7 Concurrency Cookbook 》的第三章, 作者: Javier Fernández González 譯者:鄭玉婷

等待多個併發事件完成

Java併發API提供這樣的類,它允許1個或者多個執行緒一直等待,直到一組操作執行完成。 這個類就是CountDownLatch類。它初始一個整數值,此值是執行緒將要等待的運算元。當某個執行緒為了想要執行這些操作而等待時, 它要使用 await()方法。此方法讓執行緒進入休眠直到操作完成。 當某個操作結束,它使用countDown() 方法來減少CountDownLatch類的內部計數器。當計數器到達0時,這個類會喚醒全部使用await() 方法休眠的執行緒們。

在這個指南,你將學習如果使用 CountDownLatch 類來實現 video- conference 系統。 video-conference 系統將等待全部參與者到達後才會開始。

準備

指南中的例子是使用Eclipse IDE 來實現的。如果你使用Eclipse 或者其他的IDE,例如NetBeans,開啟並建立一個新的java任務。

怎麼做呢

按照這些步驟來實現下面的例子::

//1.   建立一個類名為 Videoconference 並特別實現 Runnable 介面。這個類將實現 video-conference 系統。
public class Videoconference implements Runnable{

//2.   宣告 CountDownLatch 物件名為 controller。
private final CountDownLatch controller;

//3.   實現類的建構函式,初始 CountDownLatch 屬性。Videoconference 類接收將要等待的參與者的量為引數。
public Videoconference(int number) {
	controller=new CountDownLatch(number);
}

//4.   實現 arrive() 方法。每次有參與者到達都會呼叫此方法。它接收String型別的引數名為 name。
public void arrive(String name){

//5.   首先,它輸出某某引數已經到達。
System.out.printf("%s has arrived.",name);

//6.   然後,呼叫CountDownLatch物件的 countDown() 方法。
controller.countDown();

//7.	最後,使用CountDownLatch物件的 getCount() 方法輸出另一條關於還未確定到達的參與者數。
System.out.printf("VideoConference: Waiting for %d participants.\n",controller.getCount());

//8.   實現video-conference 系統的主方法。它是每個Runnable都必須有的 run() 方法。
@Override
public void run() {

//9.   首先,使用 getCount() 方法來輸出這次video conference的參與值的數量資訊。
System.out.printf("VideoConference: Initialization: %d participants.\n",controller.getCount());

//10. 然後, 使用 await() 方法來等待全部的參與者。由於此法會丟擲 InterruptedException 異常,所以要包含處理程式碼。
try {
controller.await();

//11. 最後,輸出資訊表明全部參與者已經到達。
System.out.printf("VideoConference: All the participants have come\n");
System.out.printf("VideoConference: Let's start...\n");
} catch (InterruptedException e) {
	e.printStackTrace();
}

//12. 建立 Participant 類並實現 Runnable 介面。這個類表示每個video conference的參與者。
public class Participant implements Runnable {

//13. 宣告一個私有 Videoconference 屬性名為 conference.
private Videoconference conference;

//14. 宣告一個私有 String 屬性名為 name。
private String name;

//15. 實現類的建構函式,初始化那2個屬性。
public Participant(Videoconference conference, String name) {
	this.conference=conference;
	this.name=name;
}

//16. 實現參與者的run() 方法。
@Override
public void run() {

//17.  首先,讓執行緒隨機休眠一段時間。
long duration=(long)(Math.random()*10);
try {
	TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
	e.printStackTrace();
}

//18. 然後,使用Videoconference 物件的arrive() 方法來表明參與者的到達。
conference.arrive(name);

//19. 最後,實現例子的 main 類通過建立一個名為 Main 的類併為其新增 main() 方法。
public class Main {

public static void main(String[] args) {

//20. 建立 Videoconference 物件名為 conference,將等待10個參與者。
Videoconference conference=new Videoconference(10);

//21. 建立 Thread 來執行這個 Videoconference 物件並開始執行。
Thread threadConference=new Thread(conference);
threadConference.start();

//22. 建立 10個 Participant 物件,為每個物件各建立一個 Thread 物件來執行他們,開始執行全部的執行緒。
for (int i=0; i<10; i++){
	Participant p=new Participant(conference, "Participant "+i);
	Thread t=new Thread(p);
	t.start();
}

它是怎麼工作的…

CountDownLatch類有3個基本元素:

  1. 初始值決定CountDownLatch類需要等待的事件的數量。
  2. await() 方法, 被等待全部事件終結的執行緒呼叫。
  3. countDown() 方法,事件在結束執行後呼叫。

當建立 CountDownLatch 物件時,物件使用建構函式的引數來初始化內部計數器。每次呼叫 countDown() 方法, CountDownLatch 物件內部計數器減一。當內部計數器達到0時, CountDownLatch 物件喚醒全部使用 await() 方法睡眠的執行緒們。

不可能重新初始化或者修改CountDownLatch物件的內部計數器的值。一旦計數器的值初始後,唯一可以修改它的方法就是之前用的 countDown() 方法。當計數器到達0時, 全部呼叫 await() 方法會立刻返回,接下來任何countDown() 方法的呼叫都將不會造成任何影響。

此方法與其他同步方法有這些不同:

CountDownLatch 機制不是用來保護共享資源或者臨界區。它是用來同步一個或者多個執行多個任務的執行緒。它只能使用一次。像之前解說的,一旦CountDownLatch的計數器到達0,任何對它的方法的呼叫都是無效的。如果你想再次同步,你必須建立新的物件。

以下截圖是例子的執行輸出:

你可以發現最後的參與者到達後,內部計數器一到達0,CountDownLatch物件就叫醒全部的 Videoconference 物件,他們全部輸出資訊表示video conference可以開始了。

更多…

CountDownLatch 類有另一種版本的 await() 方法,它是:

  • await(long time, TimeUnit unit): 此方法會休眠直到被中斷; CountDownLatch 內部計數器到達0或者特定的時間過去了。TimeUnit 類包含了:DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, 和 SECONDS.