java多線程學習-java.util.concurrent詳解(一) Latch/Barrier

 Java1.5提供了一個很是高效實用的多線程包:java.util.concurrent, 提供了大量高級工具,能夠幫助開發者編寫高效、易維護、結構清晰的Java多線程程序。從這篇blog起,我將跟你們一塊兒共同窗習這些新的Java多線程構件 

1. CountDownLatch 
    咱們先來學習一下JDK1.5 API中關於這個類的詳細介紹: 
「一個同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待。 用給定的計數 初始化 CountDownLatch。因爲調用了 countDown() 方法,因此在當前計數到達零以前,await 方法會一直受阻塞。以後,會釋放全部等待的線程,await 的全部後續調用都將當即返回。這種現象只出現一次——計數沒法被重置。若是須要重置計數,請考慮使用 CyclicBarrier。」 

    這就是說,CountDownLatch能夠用來管理一組相關的線程執行,只需在主線程中調用CountDownLatch 的await方法(一直阻塞),讓各個線程調用countDown方法。當全部的線程都只需完countDown了,await也順利返回,再也不阻塞了。在這樣狀況下尤爲適用:將一個任務分紅若干線程執行,等到全部線程執行完,再進行彙總處理。 

    下面我舉一個很是簡單的例子。 假設咱們要打印1-100,最後再輸出「Ok「。1-100的打印順序不要求統一,只需保證「Ok「是在最後出現便可。  

    解決方案:咱們定義一個CountDownLatch,而後開10個線程分別打印(n-1)*10+1至(n-1)*10+10。主線程中調用await方法等待全部線程的執行完畢,每一個線程執行完畢後都調用countDown方法。最後再await返回後打印「Ok」。 

具體代碼以下(本代碼參考了JDK示例代碼): 
Java代碼   收藏代碼
  1. import java.util.concurrent.CountDownLatch;  
  2. /** 
  3.  * 示例:CountDownLatch的使用舉例 
  4.  * Mail: ken@iamcoding.com 
  5.  * @author janeky 
  6.  */  
  7. public class TestCountDownLatch {  
  8.     private static final int N = 10;  
  9.   
  10.     public static void main(String[] args) throws InterruptedException {  
  11.         CountDownLatch doneSignal = new CountDownLatch(N);  
  12.         CountDownLatch startSignal = new CountDownLatch(1);//開始執行信號  
  13.   
  14.         for (int i = 1; i <= N; i++) {  
  15.             new Thread(new Worker(i, doneSignal, startSignal)).start();//線程啓動了  
  16.         }  
  17.         System.out.println("begin------------");  
  18.         startSignal.countDown();//開始執行啦  
  19.         doneSignal.await();//等待全部的線程執行完畢  
  20.         System.out.println("Ok");  
  21.   
  22.     }  
  23.   
  24.     static class Worker implements Runnable {  
  25.         private final CountDownLatch doneSignal;  
  26.         private final CountDownLatch startSignal;  
  27.         private int beginIndex;  
  28.   
  29.         Worker(int beginIndex, CountDownLatch doneSignal,  
  30.                 CountDownLatch startSignal) {  
  31.             this.startSignal = startSignal;  
  32.             this.beginIndex = beginIndex;  
  33.             this.doneSignal = doneSignal;  
  34.         }  
  35.   
  36.         public void run() {  
  37.             try {  
  38.                 startSignal.await(); //等待開始執行信號的發佈  
  39.                 beginIndex = (beginIndex - 1) * 10 + 1;  
  40.                 for (int i = beginIndex; i <= beginIndex + 10; i++) {  
  41.                     System.out.println(i);  
  42.                 }  
  43.             } catch (InterruptedException e) {  
  44.                 e.printStackTrace();  
  45.             } finally {  
  46.                 doneSignal.countDown();  
  47.             }  
  48.         }  
  49.     }  
  50. }  


    總結:CounDownLatch對於管理一組相關線程很是有用。上述示例代碼中就形象地描述了兩種使用狀況。第一種是計算器爲1,表明了兩種狀態,開關。第二種是計數器爲N,表明等待N個操做完成。從此咱們在編寫多線程程序時,可使用這個構件來管理一組獨立線程的執行。 

2. CyclicBarrier 
    咱們先來學習一下JDK1.5 API中關於這個類的詳細介紹: 
    「一個同步輔助類,它容許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的線程的程序中,這些線程必須不時地互相等待,此時 CyclicBarrier 頗有用。由於該 barrier 在釋放等待線程後能夠重用,因此稱它爲循環 的 barrier。 
    CyclicBarrier 支持一個可選的 Runnable 命令,在一組線程中的最後一個線程到達以後(但在釋放全部線程以前),該命令只在每一個屏障點運行一次。若在繼續全部參與線程以前更新共享狀態,此屏障操做 頗有用。 

    咱們在學習CountDownLatch的時候就提到了CyclicBarrier。二者究竟有什麼聯繫呢?引用[JCIP]中的描述「The key difference is that with a barrier, all the threads must come together at a barrier point at the same time in order to proceed. Latches are for waiting for events; barriers are for waiting for other threads。CyclicBarrier等待全部的線程一塊兒完成後再執行某個動做。這個功能CountDownLatch也一樣能夠實現。可是CountDownLatch更多時候是在等待某個事件的發生。在CyclicBarrier中,全部的線程調用await方法,等待其餘線程都執行完。 

    舉一個很簡單的例子, 今天晚上咱們哥們4個去Happy。就互相通知了一下:晚上八點準時到xx酒吧門前集合,不見不散!。有個哥們住的近,早早就到了。有的事務繁忙,恰好踩點到了。不管怎樣,先來的都不能獨自行動,只能等待全部人  

代碼以下(參考了網上給的一些教程) 
Java代碼   收藏代碼
  1. import java.util.Random;  
  2. import java.util.concurrent.BrokenBarrierException;  
  3. import java.util.concurrent.CyclicBarrier;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6.   
  7. public class TestCyclicBarrier {  
  8.   
  9.     public static void main(String[] args) {  
  10.       
  11.         ExecutorService exec = Executors.newCachedThreadPool();       
  12.         final Random random=new Random();  
  13.           
  14.         final CyclicBarrier barrier=new CyclicBarrier(4,new Runnable(){  
  15.             @Override  
  16.             public void run() {  
  17.                 System.out.println("你們都到齊了,開始happy去");  
  18.             }});  
  19.           
  20.         for(int i=0;i<4;i++){  
  21.             exec.execute(new Runnable(){  
  22.                 @Override  
  23.                 public void run() {  
  24.                     try {  
  25.                         Thread.sleep(random.nextInt(1000));  
  26.                     } catch (InterruptedException e) {  
  27.                         e.printStackTrace();  
  28.                     }  
  29.                     System.out.println(Thread.currentThread().getName()+"到了,其餘哥們呢");  
  30.                     try {  
  31.                         barrier.await();//等待其餘哥們  
  32.                     } catch (InterruptedException e) {  
  33.                         e.printStackTrace();  
  34.                     } catch (BrokenBarrierException e) {  
  35.                         e.printStackTrace();  
  36.                     }  
  37.                 }});  
  38.         }  
  39.         exec.shutdown();  
  40.     }  
  41.   
  42. }  


    關於await方法要特別注意一下,它有可能在阻塞的過程當中因爲某些緣由被中斷 

    總結:CyclicBarrier就是一個柵欄,等待全部線程到達後再執行相關的操做。barrier 在釋放等待線程後能夠重用。 
相關文章
相關標籤/搜索