多線程學習-day-07CountDownLatch、CyclicBarrier

線程基礎、線程之間的共享和協作

(目前會將一些概念簡單描述,一些重點的點會詳細描述)

學習目標:多線程的併發工具類(3)

CountDownLatch、CyclicBarrier

一、CountDownLatch

官方介紹:

CountDownLatch是在java1.5被引入的,它存在於java.util.concurrent包下。CountDownLatch這個類能夠使一個線程等待其他線程完成各自的工作後再執行。例如,應用程序的主線程希望在負責啓動框架服務的線程已經啓動所有的框架服務之後再執行。

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

 

什麼意思呢?就是運行過程中,有幾個線程,那麼只有當幾個線程同時就緒之後,類似於100米賽跑一樣,必須所有運動員到達起跑線之後,通過一個發令槍才能夠一起衝向終點,運行起來。

裏面就有幾個方法,分別介紹一下:

countDown()方法:該方法初始值設置爲允許運行的線程數,這裏比如賽道上只能容納10個人,則初始值爲10,然後每一次線程執行完,則將初始值10減1,一直減到0爲止,然後表示所有的運動員都就位了,然後就等待發令槍聲響就開始同時運行了。這裏要注意的點是,必須在每一個線程執行完之後,調用countDown()方法,否則數據將出錯!

await()方法:該方法就相當於發令槍,當判斷countDown()將初始值一直減到0 之後,表示所有的線程已經就緒了,就執行await()方法,所有線程就開始同時執行後續操作。

下面來看代碼:

import java.util.concurrent.CountDownLatch;

import com.xiangxue.tools.SleepTools;

/**
 * CountDownLatch工具類使用
 * 
 * @author xgx
 */
public class UseCountDownLatch {

	// 定義總共有7個球
	private static final int ALL_SEVEN_BALL = 7;

	// 實例化CountDownLatch類
	private static CountDownLatch countDownLatch = new CountDownLatch(ALL_SEVEN_BALL);

	/**
	 * 調用CountDownLatch實例
	 * 
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i <= ALL_SEVEN_BALL; i++) {
			Thread thread = new Thread(new SubCountDownLatch());
			thread.start();
		}
		// 當所有線程已經執行完畢,調用await()方法,將同時線程置於非阻塞狀態,執行後續的業務
		countDownLatch.await();
		System.out.println("已經成功集齊7色球!恭喜中大獎!");
	}

	// 定義CountDownLatch多線程類,表示這個類要做什麼事情
	private static class SubCountDownLatch implements Runnable {

		// 所有7個線程countDown()方法沒有把總數減爲0時,都將阻塞,當通過countDown()方法將數量減到0時,則所有的線程都已經執行完畢,進行輸出
		public void run() {
			// 這裏輸出我意思了一下。至於爲什麼Thread.currentThread().getId()是從10開始的,自行百度,這裏不多說。
			System.out.println("已經蒐集到了第: " + (Long.valueOf(Thread.currentThread().getId()) - 9) + " 個球");
			SleepTools.ms(1000);
			countDownLatch.countDown();
		}
	}
}

控制檯輸出結果:
已經蒐集到了第: 6 個球
已經蒐集到了第: 5 個球
已經蒐集到了第: 4 個球
已經蒐集到了第: 2 個球
已經蒐集到了第: 7 個球
已經蒐集到了第: 3 個球
已經蒐集到了第: 1 個球
已經成功集齊7色球!恭喜中大獎!

我們看到,所有線程都是按照順序來執行的,沒有出現線程不安全結果。

二、CyclicBarrier

官方介紹:

CyclicBarrier是一個同步輔助類,它允許一組線程互相等待,直到所有線程都到達某個公共屏障點(也可以叫同步點),即相互等待的線程都完成調用await方法,所有被屏障攔截的線程纔會繼續運行await方法後面的程序。在涉及一組固定大小的線程的程序中,這些線程必須不時地互相等待,此時CyclicBarrier很有用。因爲該屏障點在釋放等待線程後可以重用,所以稱它爲循環的屏障點。CyclicBarrier支持一個可選的Runnable命令,在一組線程中的最後一個線程到達屏障點之後(但在釋放所有線程之前),該命令只在所有線程到達屏障點之後運行一次,並且該命令由最後一個進入屏障點的線程執行。

什麼意思呢?CyclicBarrier強調的是n個線程,大家相互等待,只要有一個沒完成,所有人都得等着。還是舉上面的例子,這次就是得7個人,然後去買cai票,然後等最後一個買完,7個人都到一起之後(這裏說到達屏障點之後),就可以運行後續的程序了。

來看實現代碼:

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier工具類使用
 * 
 * @author xgx
 */
public class UseCyclicBarrier {

	// 定義總共有7個球
	private static final int ALL_SEVEN_BALL = 7;

	// 定義CyclicBarrier對象
	private static CyclicBarrier cyclicBarrier = new CyclicBarrier(ALL_SEVEN_BALL, new ResultCyclicBarrier());

	// 主方法
	public static void main(String[] args) {
		for (int i = 0; i < ALL_SEVEN_BALL; i++) {
			Thread thread = new Thread(new OperateCyclicBarrier());
			thread.start();
		}
	}

	// 定義一個最終結果的類
	private static class ResultCyclicBarrier implements Runnable {
		public void run() {
			// 調用操作類,然後都在屏障點等候
			new OperateCyclicBarrier();
			// 當ALL_SEVEN_BALL個人都已經成功操作完成之後,在執行後續業務
			System.out.println("已經成功集齊7色球!恭喜中大獎!");
		}
	}

	// 定義一個ALL_SEVEN_BALL個數線程操作類
	private static class OperateCyclicBarrier implements Runnable {
		// 做ALL_SEVEN_BALL個數的事情
		public void run() {
			try {
				System.out.println("第:" + Long.valueOf(Thread.currentThread().getId() - 9) + " 個人開始出發收集七色球!!!");
				Thread.sleep(new Random().nextInt(3000));
				// 所有的線程全部到達屏障點之前,都處於阻塞狀態
				cyclicBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}
}

控制檯輸出結果:
第:2 個人開始出發收集七色球!!!
第:6 個人開始出發收集七色球!!!
第:3 個人開始出發收集七色球!!!
第:1 個人開始出發收集七色球!!!
第:4 個人開始出發收集七色球!!!
第:7 個人開始出發收集七色球!!!
第:5 個人開始出發收集七色球!!!
已經成功集齊7色球!恭喜中大獎!

三、CountDownLatch和CyclicBarrier的區別

(1)CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。所以CyclicBarrier能處理更爲複雜的業務場景,比如如果計算髮生錯誤,可以重置計數器,並讓線程們重新執行一次。(這裏CyclicBarrier更多的方法這裏後興趣的朋友可以自行研究)

(2)CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。比如以下代碼執行完之後會返回true。

(3)CountDownLatch會阻塞主線程,CyclicBarrier不會阻塞主線程,只會阻塞子線程。

(4)CountDownLatch依靠一個外力(計數器,發令槍)來控制線程,而CyclicBarrier是相當於本身來控制線程,舉個例子:

        CountDownLatch:有個裝滿寶石的房間,門外有7把鎖,然後有7個人要進入房間組成隊伍A,還有另外7個人手裏拿着鑰匙組成隊伍B,那麼首先的A組7人必須等到B組7人把鑰匙送過來,然後把門外7把鎖給分別打開後,這A組7人才能夠進入房間拿到寶石。

        CyclicBarrier:有個裝滿寶石的房間,門外也有7把鎖,然後這7個人必須到另外一個房間,各自完成一個任務,然後每個人才能夠獲得一把鎖,完成任務之後,這7個人就能夠打開那7把鎖,然後拿到寶石。

 

因爲時間有限,每天晚上只能總結一點,因此希望能夠理解思想,繼續學習進步!!

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