前言
在日常處理線程同步問題的時候我們經常聯想到的可能有下面幾種辦法:
1.synchronized關鍵字
2.Java5引入的java.util.concurrent.locks包的顯示鎖
3.CountDownLatch
今天要說的CountDownLatch是通過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。
簡單的來說,CountDownLatch就是可以通過這種計數的方式控制線程的執行臨界區,舉個例子:
我有事務A、B、C、D需要完成,A、B、C是D的前置條件,D事務必須要等A、B、C全部完成後才能開始。
我們就可以通過CountDownLatch去管理這種執行上的先後問題。
實現
打開java.util.concurrent包的CountDownLatch類,可以看到有以下成員方法。
幾個關鍵的方法:
- CountDownLatch(int)
初始化一個CountDownLatch,設置閂值,如果閂值未減到0,不執行後續語句
- await()
判斷CountDownLatch的值是否已經減到了0,沒有的話將一直處於等待狀態
- countDown()
供線程完成自己的業務後調用,CountDownLatch初始化的值減1
- getCount()
獲取當前CountDownLatch的值
看了CountDownLatch具體的實現方法,關於前言中的例子如何實現,是不是就已經胸有成竹了,我們只需要做下面幾件事。
1.爲A、B、C三個事務各聲明一個線程去執行相關業務代碼。
2.聲明一個初始值爲3的CountDownLatch,用來計數。
3.A、B、C三個事務的線程在執行完成後調用countDown()方法。
4.通過CountDownLatch的await()方法判斷A、B、C是否均已完成。
示例
針對上面的例子,模擬了一個應用場景。
假設某個新上線項目的部署需要先啓動網絡服務、DNS服務以及數據服務,在其他三個服務啓動前,部署事務一直處於閉鎖狀態。
- Service.java
服務類,聲明3個服務對象。
package countDownLatch.service;
/**
*
* @author lenovo
* 模擬服務啓動
*/
public class Service {
//服務名
private String serviceName;
//服務啓動所需時間
private Long startUpMills;
public Service(String serviceName, Long startUpMills){
this.serviceName = serviceName;
this.startUpMills = startUpMills;
}
public void startUpService(){
try{
System.out.println("服務"+serviceName+"啓動中...");
Thread.sleep(startUpMills);
}catch(InterruptedException ie){
ie.printStackTrace();
}
System.out.println("服務"+serviceName+"啓動完畢...");
}
}
- ServiceStartupTestThread.java
服務線程類,將每個服務的啓動工作交付給線程執行,並完成服務啓動前後的相關操作。
package countDownLatch.service;
import java.util.concurrent.CountDownLatch;
public class ServiceStartupTestThread implements Runnable{
//當前線程內的服務對象
private Service service;
//countDownLatch計數器
private CountDownLatch cdl;
ServiceStartupTestThread(Service service, CountDownLatch cdl){
this.service = service;
this.cdl = cdl;
}
@Override
public void run() {
//處理線程綁定的業務邏輯
service.startUpService();
//處理完畢CountDownLatch-1
cdl.countDown();
}
}
- StartupProject.java
啓動項目主方法,在這裏判斷三個服務是否均已啓動完畢。
package countDownLatch.service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class StartupProject {
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(3);
//創建服務對象
Service netWork = new Service("網絡服務", (long)(Math.random()*5000+1000));
Service dns = new Service("DNS服務", (long)(Math.random()*5000+1000));
Service dataSource = new Service("數據服務", (long)(Math.random()*5000+1000));
//爲每一個服務提供執行線程
ServiceStartupTestThread netWorkThread = new ServiceStartupTestThread(netWork, cdl);
ServiceStartupTestThread dnsThread = new ServiceStartupTestThread(dns, cdl);
ServiceStartupTestThread dataSourceThread = new ServiceStartupTestThread(dataSource, cdl);
//創建線程list
List<ServiceStartupTestThread> serviceList = new ArrayList<ServiceStartupTestThread>();
serviceList.add(netWorkThread);
serviceList.add(dnsThread);
serviceList.add(dataSourceThread);
//創建一個容量適配的連接池
ExecutorService executor = Executors.newFixedThreadPool(serviceList.size());
for(ServiceStartupTestThread t : serviceList){
executor.execute(t);
}
//關閉連接池
executor.shutdown();
try {
cdl.await();
System.out.println("所有服務已經啓動完畢!");
//method()啓動項目部署...
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果:
參考文章
感謝!
http://www.importnew.com/15731.html
什麼時候使用CountDownLatchhttp://blog.csdn.net/jackfrued/article/details/44499227
關於Java併發編程的總結和思考