閉鎖(latch)是一種Synchronizer(Synchronizer:是一個對象,它根據本身的狀態調節線程的控制流。常見類型的Synchronizer包括信號量、關卡和閉鎖)。
閉鎖可以延遲線程的進度直到線程線程到達終止狀態。一個閉鎖工作起來就像是一道大門:直到閉鎖達到終點狀態之前,門一直是關閉的,沒有線程能夠通過,在終點狀態到來的時候,所有線程都可以通過。
閉鎖可以用來確保特定活動直到其他的活動都完成後才開始發生,比如:
CountDownLatch是一個同步輔助類,存在於java.util.concurrent包下,靈活的實現了閉鎖,它允許一個或多個線程等待一個事件集的發生。
CountDownLatch是通過一個計數器來實現的,計數器的初始值為線程的數量。每當一個線程完成了自己的任務後,計數的值就會減1。當計數器值到達0時,它所表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。
CountDownLatch構造函數:
CountDownLatch(int count);
構造器中計數值(count)就是閉鎖需要等待的線程數量,這個值只能被設置一次。
CountDownLatch類的方法:
與CountDownLatch第一次交互是主線程等待其它的線程,主線程必須在啟動其它線程後立即調用await方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務。
其他的N個線程必須引用閉鎖對象,因為他們需要通知CountDownLatch對象,他們已經完成了各自的任務,這種機制就是通過countDown()方法來完成的。每調用一次這個方法,在構造函數中初始化的count值就減1,所以當N個線程都調用了這個方法count的值等於0,然後主線程就能通過await方法,恢復自己的任務。
下面介紹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();// 工人完成工作,計數器減一 } } } }
執行的結果為:
2、Java並發編程實踐