1. 程式人生 > >多執行緒學習-day-07

多執行緒學習-day-07

執行緒基礎、執行緒之間的共享和協作

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

學習目標:多執行緒的併發工具類(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把鎖,然後拿到寶石。

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