【Java並發編程三】閉鎖


1、什么是閉鎖?

  閉鎖(latch)是一種Synchronizer(Synchronizer:是一個對象,它根據本身的狀態調節線程的控制流。常見類型的Synchronizer包括信號量、關卡和閉鎖)。

  閉鎖可以延遲線程的進度直到線程線程到達終止狀態。一個閉鎖工作起來就像是一道大門:直到閉鎖達到終點狀態之前,門一直是關閉的,沒有線程能夠通過,在終點狀態到來的時候,所有線程都可以通過。

2、應用場景

  閉鎖可以用來確保特定活動直到其他的活動都完成后才開始發生,比如:

  1. 確保一個計算不會執行,直到它所需要的資源被初始化
  2. 確保一個服務不會開始,直到它依賴的其他服務都已經開始
  3. 等待,直到活動的所有部分都為繼續處理做好充分准備
  4. 死鎖檢測,可以使用n個線程訪問共享資源,在每次測試階段的線程數目是不同的,並嘗試產生死鎖

3、閉鎖的實現

  CountDownLatch是一個同步輔助類,存在於java.util.concurrent包下,靈活的實現了閉鎖,它允許一個或多個線程等待一個事件集的發生。

  CountDownLatch是通過一個計數器來實現的,計數器的初始值為線程的數量。每當一個線程完成了自己的任務后,計數的值就會減1。當計數器值到達0時,它所表示所有的線程已經完成了任務,然后在閉鎖上等待的線程就可以恢復執行任務。

4、CountDownLatch原理

  CountDownLatch構造函數:

CountDownLatch(int count);

  構造器中計數值(count)就是閉鎖需要等待的線程數量,這個值只能被設置一次。

  CountDownLatch類的方法:

  • void await():使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷。
  • boolean await(long timeout, TimeUnit unit):使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷或超出了指定的等待時間。 
  • void countDown():遞減鎖存器的計數,如果計數到達零,則釋放所有等待的線程。
  • long getCount():返回當前計數。 
  • String toString():返回標識此鎖存器及其狀態的字符串。

  與CountDownLatch第一次交互是主線程等待其它的線程,主線程必須在啟動其它線程后立即調用await方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務。

  其他的N個線程必須引用閉鎖對象,因為他們需要通知CountDownLatch對象,他們已經完成了各自的任務,這種機制就是通過countDown()方法來完成的。每調用一次這個方法,在構造函數中初始化的count值就減1,所以當N個線程都調用了這個方法count的值等於0,然后主線程就能通過await方法,恢復自己的任務。

 5、CountDownLatch使用案例

  下面介紹API中推薦的一種典型的用法。

  首先給出兩個類,其中一組worker線程使用了兩個倒計數鎖存器。

  第一個類是一個啟動信號,在driver為繼續執行worker做好准備之前,它會阻止所有的worker繼續執行。

  第二個類是一個完成信號,它允許driver在完成所有的worker之前一直等待。

class Driver 
{ // ...
      private final static int N=10;
      public static  void main(String[] args) throws InterruptedException 
       {
         CountDownLatch startSignal = new CountDownLatch(1);
         CountDownLatch doneSignal = new CountDownLatch(N);

         for (int i = 0; i < N; ++i) // create and start threads
           new Thread(new Worker(startSignal, doneSignal)).start();

         doSomethingElse();            // don't let run yet
         startSignal.countDown();      // let all threads proceed
         doSomethingElse();
         doneSignal.await();           // wait for all to finish
       }
    private static void doSomethingElse()
    {
        //doSomethingElse;
    }
}
class Worker implements Runnable
{
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;

    Worker(CountDownLatch startSignal, CountDownLatch doneSignal)
    {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }
    public void run()
    {
        try
        {
            startSignal.await();
            doWork();
            doneSignal.countDown();
        } 
        catch (InterruptedException ex)
        {
        } // return;
    }
    void doWork()
    {
        // ...
    }
}

  基於上面的模型,下面我實現了自己的應用場景:

public class CountDownLatchDemo
{
     final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    public static void main(String[] args) throws InterruptedException
    {
        CountDownLatch startLatch = new CountDownLatch(1);//開始工作
        CountDownLatch endLatch = new CountDownLatch(2);// 兩個工人的協作
    
        Worker worker1 = new Worker("zhang san", 5000,startLatch, endLatch);
        Worker worker2 = new Worker("li si", 8000, startLatch,endLatch);
        
        worker1.start();//
        worker2.start();//

        startLatch.countDown(); //工作開始
        endLatch.await();// 等待所有工人完成工作
        System.out.println("all work done at " + sdf.format(new Date()));
    }
    
    static class Worker extends Thread
    {
        String workerName;
        int workTime;
        CountDownLatch startLatch;
        CountDownLatch endLatch;
        public Worker(String workerName, int workTime, CountDownLatch startLatch,CountDownLatch endLatch)
        {
            this.workerName = workerName;
            this.workTime = workTime;
            this.startLatch=startLatch;
            this.endLatch = endLatch;
        }
        public void run()
        {
            try
            {
                startLatch.await(); //等待開始工作時間
                System.out.println("Worker " + workerName + " do work begin at "+ sdf.format(new Date()));
                Thread.sleep(workTime);
            } 
            catch (InterruptedException e)
            {
                // TODO 自動生成的 catch 塊
                e.printStackTrace();
            }
            finally
            {
                System.out.println("Worker " + workerName + " do work complete at " + sdf.format(new Date()));
                endLatch.countDown();// 工人完成工作,計數器減一
            }
        }
    }
}

  執行的結果為:

  

六、參考資料

  1、http://www.importnew.com/15731.html

  2、Java並發編程實踐


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com