java多線程之CountDownLatch倒數閘門

  在多個線程進行協做時,一個常見的情景是一個線程須要等待另外的線程完成某些任務以後才能繼續進行.在這種狀況下,可使用CountDownLatch類,CountDownLatch類至關於多個線程等待開啓的一個閘門.只有在其餘線程完成任務以後,閘門纔會打開,等待的線程才能運行.在建立CountDownLatch類的對象是須要指定等待完成的任務數目.一個CountDownLatch.類的對象被執行任務的線程和等待任務完成的線程說共享.當執行任務的線程完成其任務時,調用countDown方法來使待完成的任務數量減1.等待任務完成的線程經過調用await方法進入阻塞狀態直到待完成的任務數量變爲0.當全部任務都完成時,等待任務完成的線程會從await方法返回,能夠繼續執行後繼的代碼.CountDownLatch類的對象的使用是一次性的.一旦待完成的任務數量變爲0,再調用await方法就再也不阻塞當前線程,而是當即返回.
倒數閘門的使用示例:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import  java.util.concurrent.CountDownLatch;
 
/**
  * create at 11-9-17
  *
  * @author KETQI
  * @category CountDownLatch主要起倒計時計數器做用,它主要有兩個方法await()和countDown()。
  * 一旦某個線程調用await()方法,那麼該線程就會阻塞,等待CountDownLatch計數器倒計時歸零,
  * 須要注意的是儘管線程調用await()方法後會阻塞,
  * 可是CountDownLatch容許別的線程調用countDown()方法,將計數器減一。
  * 也就是說調用計時器的線程阻塞後,能夠利用別的線程控制調用線程什麼時候重新開始運行。
  * <p/>
  * 該demo主要想要作的事就是:在主線程中建立N個子線程,讓支線程等待主線程將開關計數器startSignal打開。
  * 而當主線程打開startSignal開關後,主線程要等待計數器doneSignal歸零,
  * 而doneSignal計數器歸零依賴於每一個支線程爲主線程的計數器減一。
  * 因此當主線程打開開關後,支線程才能運行完畢,而只有支線程所有運行完畢,才能打開主線程的計數器。
  * 這樣整個程序才能走完
  */
public  class  CountDownLatchDemo {
     public  static  final  int  N =  5 ;
 
     public  static  void  main(String[] args)  throws  InterruptedException {
         // 用於向工做線程發送啓動信號,由主線程調用
         CountDownLatch startSignal =  new  CountDownLatch( 1 );
         // 用於等待工做線程的結束信號,由子線程調用
         CountDownLatch doneSignal =  new  CountDownLatch(N);
         // 建立啓動線程
         System.out.println( "開始建立並運行分支線程,且分支線程啓動startSignal計數器,等待主線程將startSignal計數器打開" );
         for  ( int  i =  0 ; i < N; i++) {
             new  Thread( new  LatchWorker(startSignal, doneSignal),  "t"  + i).start();
         }
 
         // 主線程,遞減開始計數器,讓全部線程開始工做
         System.out.println( "主線程"  + Thread.currentThread().getName() +  "將startSignal計數器打開" );
         startSignal.countDown();
         // 主線程阻塞,等待全部線程完成
         System.out.println( "主線程"  + Thread.currentThread().getName() +  "開始倒計時5個數" );
         doneSignal.await();
         /**
          * 爲何說運行到下一句,全部線程就所有運行完畢了呢。 由於主線程要倒計時5個數, 而產生的5個支線程在運行完畢前會將主線程的計數器減一,
          * 因此若是全部支線程運行完畢了 ,主線程才能繼續運行主線程的最後一個打印程序
          */
         System.out.println( "全部線程運行完畢" );
     }
}
 
class  LatchWorker  implements  Runnable {
     // 用於等待啓動信號
     private  final  CountDownLatch startSignal;
     // 用於發送結束信號
     private  final  CountDownLatch doneSignal;
 
     LatchWorker(CountDownLatch startSignal, CountDownLatch doneSignal) {
         this .startSignal = startSignal;
         this .doneSignal = doneSignal;
     }
 
     public  void  run() {
         try  {
             // 一旦調用await()方法,該線程就會開始阻塞。知道計數器startSignal爲0
             System.out.println(Thread.currentThread().getName() +  " 開始調用await()方法,等待計數器startSignal被主線程打開" );
             startSignal.await();
             doWork();
             System.out.println(Thread.currentThread().getName() +  " 將主線程的計數器減一" );
             doneSignal.countDown(); // 發送完成信號
         catch  (InterruptedException ex) {
             ex.printStackTrace();
         }
     }
 
     void  doWork() {
         System.out.println(Thread.currentThread().getName() +  " 的計數器被打開,分支線程開始運行" );
         try  {
             Thread.sleep(( long ) Math.random() *  10000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
}
相關文章
相關標籤/搜索