高併發(8)- 線程併發工具類-CountDownLatch

高併發(8)- 線程併發工具類-CountDownLatch

前言

    上篇文章講解了線程的併發工具類之ForkJoin,本文就來講解CountDownLatch併發工具類
    ForkJoin的核心思想是分而治之
    CountDownLatch的則是一組線程等待其他的線程完成工作以後在執行。

什麼是CountDownLatch

CountDownLatch是一組線程等待其他線程工作完成以後在執行。例如:一個框架在啓動的時候,需要加載各種功能,只有當這些功能加載完成之後,纔可以運行主線程,這個時候就可以用到CountDownLatch了,CountDownLatch通過計數器來實現,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數器的值爲0時,表示所有線程都執行完畢,然後在閉鎖上等待的線程就可以恢復工作了

CountDownLatch執行流程
如圖所示,我們定義了一個計數器cnt = 5,TW1和TW2兩個線程執行了await()方法,就需要等待計數器減少爲0.

  • 所以Td執行了一個操作,CountDown了一次,cnt-1=4
  • Tb線程也操作了一次,CountDown了一次,cnt-1=3
  • Td線程又操作了一次,CountDown了一次,cnt-1=2
  • Ta線程也操作了一次,CountDown了一次,cnt-1=1
  • Tc線程也操作了一次,CountDown了一次,cnt-1=0
    因爲這時候cnt計數器=0所以,TW1和TW2兩個線程也便推出了等待,繼續運行

注意

  1. 一個CountDownLatch可以被多個線程等待,而不是隻能被一個線程使用,如圖所示TW1和TW2用的同一個CountDownLatch。
  2. 一個線程也可以多次CountDown操作,如圖所示,Td線程進行了兩次CountDown操作。
  3. 一個線程在CountDown之後還是可以繼續運行的,不會在CountDown之後停止,如圖所示,Ta和Td兩個線程在CountDown之後繼續運行。

CountDownLatch實現

  • countDownLatch類中只提供了一個構造器:
//參數count爲計數值
public CountDownLatch(int count) {}; 
  • countDownLatch類中還有三個方法比較重要
//調用await()方法的線程會被掛起,它會等待直到count值爲0才繼續執行
public void await() throws InterruptedException { };   
//和await()類似,只不過等待一定的時間後count值還沒變爲0的話就會繼續執行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
//將count值減1
public void countDown() { };  
  • 具體代碼實現
/**
 * @version 1.0
 * @Description CountDownLatchDemo
 * @Author wb.yang
 */
public class CountDownLatchDemo {

	/**
	 * 創建一個計數器爲6的CountDownLatch
	 */
	static CountDownLatch latch = new CountDownLatch(6);


	/**
	 * 初始化線程(只有一步,有4個)
	 */
	private static class InitThread implements Runnable {

		@Override
		public void run() {
			System.out.println("Thread_" + Thread.currentThread().getId()
					+ " 準備初始化工作......");
			//初始化線程完成工作了,countDown方法只扣減一次;
			latch.countDown();
			System.out.println("Thread_" + Thread.currentThread().getId()
					+ " .繼續做剩餘的工作");
		}
	}

	/**
	 * 業務線程
	 */
	private static class BusinessThread implements Runnable {

		@Override
		public void run() {
			try {
				// 等待
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("BusiThread_" + Thread.currentThread().getId()
					+ "運行中-----");
		}
	}


	public static void main(String[] args) throws InterruptedException {
		//單獨的初始化線程,初始化分爲2步,需要扣減兩次
		new Thread(new Runnable() {
			@Override
			public void run() {
				SleepTools.ms(1);
				System.out.println("Thread_" + Thread.currentThread().getId()
						+ "準備初始化工作第一步......");
				latch.countDown();//每完成一步初始化工作,扣減一次
				System.out.println("開始第二步工作.......");
				SleepTools.ms(1);
				System.out.println("Thread_" + Thread.currentThread().getId()
						+ "準備初始化工作第二步......");
				latch.countDown();//每完成一步初始化工作,扣減一次
			}
		}).start();
		//開始主要業務線程
		new Thread(new BusinessThread()).start();
		for (int i = 0; i <= 3; i++) {
			//執行三次初始化工作
			Thread thread = new Thread(new InitThread());
			thread.start();
		}

		latch.await();
		System.out.println("main方法工作........");
	}
}

代碼中可以看到,我們定義了一個CountDownLatch,計數器爲6,然後以一個業務線程,需要等待CountDownLatch執行完之後才能執行。還有六個初始化線程。

  1. 運行一個初始化線程
  2. 運行業務線程
  3. 進行剩下的四初始化線程
  4. 主線程等待await,等待CountDownLatch

再看下運行結果
CountDownLatch運行結果
我們可以看到,先是初始化線程運行,進行了兩次CountDown,證明一個線程可以多次CountDown,然後是剩下四個初始化線程進行CountDown操作,並且在CountDown操作後技術執行代碼,然後是主業務線程在CountDown爲0的時候執行,由於主線程也是await,所以也需要CountDown爲0纔可以執行,也證明了一個CountDownLatch可以被多個線程使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章