Java基礎--併發實用工具(2)

1.同步器之CountDownLatch

類的名稱的中文翻譯爲:倒數閂(倒數鎖存器)。操作線程時,有時候我們希望這個線程進行等待,直到一定數量的事件發生之後爲止。爲了處理這種情況,併發API提供了CountDownLatch類,CountDownLatch在創建時指定要等待的事件的數量,在釋放鎖存器(閂)之前,必鬚髮生指定數量的事件。每發生一個事件,計數器遞減,當計數器減爲0時,鎖存器打開,等待的線程被喚醒,接着走。
由上可知,使用的方式很簡單:創建一個公用倒數鎖存器,在一個線程的需要等待的位置進行await(可以指定等待的最長時間,使用方法await(long wait,TimeUnit tu)wait指定等待的時長,tu指定時長的時間單位,是一個枚舉類型的一個值,在後續博文介紹這個枚舉類型),直到鎖存器打開;其他線程使用同樣的鎖存器,只是完成一個指定的事件後,調用countDown,使計數器遞減。
實例代碼如下:
import java.util.concurrent.CountDownLatch;

public class CountDownLetchTest {

	public static void main(String[] args) {
		//創建一個鎖存器,要開啓該鎖存器必須等待5個事件的發生
		CountDownLatch countDownLetch = new CountDownLatch(5);
		//線程一是使用鎖存器的線程
		new Thread(()->{
			try {
				countDownLetch.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("五個事件執行完畢,所以才輸出了這句話哦..");
		}).start();
		new Thread(()->{
			System.out.println("第一個事件執行完畢");
			countDownLetch.countDown();
		}).start();
		new Thread(()->{
			System.out.println("第二個事件執行完畢");
			countDownLetch.countDown();
		}).start();
		new Thread(()->{
			System.out.println("第三個事件執行完畢");
			countDownLetch.countDown();
		}).start();
		new Thread(()->{
			System.out.println("第四個事件執行完畢");
			countDownLetch.countDown();
		}).start();
		new Thread(()->{
			System.out.println("第五個事件執行完畢");
			countDownLetch.countDown();
		}).start();
	}
//	輸出結果:
//	第一個事件執行完畢
//	第二個事件執行完畢
//	第三個事件執行完畢
//	第四個事件執行完畢
//	第五個事件執行完畢
//	五個事件執行完畢,所以才輸出了這句話哦..
	//根據輸出結果,如果沒有等待的話,出現這種結果的可能性很小很小很小很小
}

2.同步器之CyclicBarrier

類的名稱的中文翻譯爲:生命週期攔截柵欄(週期屏障)。『真心感覺還是不要翻譯的好。。。』在併發編程中,多個線程必須在預定的執行點進行等待,直到所有的線程都到達了各自的執行點,然後,屏障打開,所有線程都接着執行,只要有一個還沒到就接着等,直到所有都到了。
由上可知,使用方式很簡單:這個類有兩個構造器分別爲:CyclicBarrier(int numThreads) CyclicBarrier(int numThreads,Runnable action)第一個參數指定需要到達執行點的線程數量,第二個參數指定全部線程到達之後要執行的操作,然後需要同步的線程使用同一個CyclicBarrier,每個到達執行點的線程進行等待await()(也可指定時間),等指定數量的線程到達後,屏障打開,如果有指定的操作執行,這個操作也會執行。
未指定屏障打開後後續操作的實例代碼:
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierWithoutSpecificAfterwardAction {

	public static void main(String[] args) {
		//不指定全部到達等待界點後要執行的操作
		CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
		new Thread(()->{
			System.out.println("I'm thread-0,I'm on the await-point..");
			try {
				cyclicBarrier.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("Ok,Every thread is here, I'm thread-0,I'll go...");
		}).start();
		new Thread(()->{
			System.out.println("I'm thread-1,I'm on the await-point..");
			try {
				cyclicBarrier.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("Ok,Every thread is here, I'm thread-1,I'll go...");
		}).start();
		new Thread(()->{
			System.out.println("I'm thread-2,I'm on the await-point..");
			try {
				cyclicBarrier.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("Ok,Every thread is here, I'm thread-2,I'll go...");
		}).start();
//		輸出結果:
//		I'm thread-0,I'm on the await-point..
//		I'm thread-1,I'm on the await-point..
//		I'm thread-2,I'm on the await-point..
//		Ok,Every thread is here, I'm thread-2,I'll go...
//		Ok,Every thread is here, I'm thread-0,I'll go...
//		Ok,Every thread is here, I'm thread-1,I'll go...
	}
}
指定了屏障打開後後續操作的實例代碼:
public class CyclicBarrierWithSpecificAfterwardAction {

		public static void main(String[] args) {
			//指定全部到達等待界點後要執行的操作
			CyclicBarrier cyclicBarrier = new CyclicBarrier(3,()->{
				System.out.println("Ok,Every thread is here,Hope you every thread go dwon smoothly...");
			});
			new Thread(()->{
				System.out.println("I'm thread-0,I'm on the await-point..");
				try {
					cyclicBarrier.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("Ok,Every thread is here, I'm thread-0,I'll go...");
			}).start();
			new Thread(()->{
				System.out.println("I'm thread-1,I'm on the await-point..");
				try {
					cyclicBarrier.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("Ok,Every thread is here, I'm thread-1,I'll go...");
			}).start();
			new Thread(()->{
				System.out.println("I'm thread-2,I'm on the await-point..");
				try {
					cyclicBarrier.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("Ok,Every thread is here, I'm thread-2,I'll go...");
			}).start();
//			輸出結果:
//			I'm thread-0,I'm on the await-point..
//			I'm thread-1,I'm on the await-point..
//			I'm thread-2,I'm on the await-point..
//			Ok,Every thread is here,Hope you every thread go dwon smoothly...
//			Ok,Every thread is here, I'm thread-2,I'll go...
//			Ok,Every thread is here, I'm thread-0,I'll go...
//			Ok,Every thread is here, I'm thread-1,I'll go...

	}

}

3.同步器之Exchanger

Exchanger類的中文翻譯爲:交換器。可能最有趣的同步器就是Exchanger,其設計的目的是爲了簡化兩個線程之間的數據交換。Exchanger對象的操作十分簡單:簡單的進行等待,直到兩個獨立的線程都調用了exchanger方法爲止,此時,進行數據交換。
構造器爲:Exchanger<V>,V指定要交換的數據的類型。
交換方法爲:V exchanger(V Object)/V exchanger(V Object,long wait,TimeUnit tu)
由以上可知使用的過程大致爲:創建一個共用的Exchanger,然後兩個線程在各自該交換數據的執行點調用exchanger方法,使用相應的數據類型接收交換過來的數據即可。
實例代碼如下:
import java.util.concurrent.Exchanger;

public class ExchangeTest {

	public static void main(String[] args) {
		Exchanger<Integer> exchanger = new Exchanger<Integer>();
		new Thread(()->{
			int count = 1;
			while(count<5){
				try {
					Integer receive = exchanger.exchange(count*2);
					System.out.println("Got from thread-1: "+receive);
					count++;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
		new Thread(()->{
			int count = 1;
			while(count<5){
				try {
					Integer receive = exchanger.exchange(count*1);
					System.out.println("Got form thread-0: "+receive);
					count++;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
//	運行結果:
//	Got from thread-1: 1
//	Got form thread-0: 2
//	Got from thread-1: 2
//	Got form thread-0: 4
//	Got form thread-0: 6
//	Got from thread-1: 3
//	Got from thread-1: 4
//	Got form thread-0: 8
}

4.同步器之Phaser

Phaser的中文翻譯爲:移相器(階段同步器)。簡單來看,Phaser是CyclicBarrier的增強版,CyclicBarrier是在一個執行點進行等待,當指定數量的線程到達這個執行點之後,這些線程就接着往下走了,而Phaser不同的是,過了這個執行點之後還可以有下一個執行點等待,即是分階段等待。主要應用場景是:允許表示一個或多個活動階段的線程進行同步。
構造器:Phaser()/Phaser(int numThreads):參數表示需要同步的線程的數量,如果一開始沒有指定也沒有關係,在程序的運行過程中,需要同步的線程的數量是可以變化的,變化的方式見下文
註冊到同步器上:int register()返回值是註冊到這個階段同步器的哪個階段上了,這裏雖然是調用了註冊方法,實際上階段同步器並不知道到底是哪個線程註冊了,只知道某個階段需要同步的線程的數量增加了一個
到達:int arrive()/int arriveAndAwaitAdvance()/int arriveAndDeregister()
三個到達方法,返回值都是當前階段編號。第一個方法僅僅是告訴調用線程,我到咯,但是我不等其他線程哦,也就是不等這個階段完成,接着往下走;第二個是我到咯,我還要等,直到指定數量的線程到達了執行點,也就是這個階段完成了,我才接着往下走;第三個方式是,我到咯,而且巧的是我到這一階段就不需要再同步了,所以在我到達這一階段我的執行點之後就註銷註冊了,那下一階段需要到達指定執行點的線程數量減一,這裏的註銷僅僅是告訴階段同步器下一階段需要同步的線程的數量減一,到底減少了哪個線程並不知道。
當然,也可以讓階段同步器提前失效,即在階段同步器運行到一定條件下就失去同步效果,這個可以通過重寫Phaser類的boolean onAdvance()方法,可用來控制當前階段同步器是否要失效的兩個參數分別是執行階段和當前註冊的需要同步線程的數量。在階段同步器向下階段推進的時候,都會調用這個方法,如果返回TRUE,就立即失效,如果返回FALSE,接着向下一階段推進。
使用的實例代碼如下:
import java.util.concurrent.Phaser;

public class PhaserTest {

	public static void main(String[] args) {
		//也可以不重寫OnAdvace()方法,重寫是爲了在執行了特定的階段之後返回,這裏是執行到第三步返回,如果沒有重寫,直到註冊的所有線程運行完了所有階段也能正常結束
		//傳到構造器中的3是總共有3個線程註冊到這個同步器上,問題來了,有時候並不是三個線程一直協同到最後,可能走着走着就只剩一個線程在起作用了,當一個線程到達之後並且不需要再同步的時候,達到之後註銷即可,調用的方法爲:
		Phaser phaser = new Phaser(3){
			@Override
			protected boolean onAdvance(int phase,int parties){
				if(phase==2||parties==0)return true;
				return false;
			}
		};
		new Thread(()->{
			//開始第一階段
			//獲取當前是在第幾階段,如果已經結束則返回負數
			int i = phaser.getPhase();
			System.out.println("thread-0 phaser--"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第二階段
			i = phaser.getPhase();
			System.out.println("thread-0 phaser---"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第三階段
			i = phaser.getPhase();
			System.out.println("thread-0 phaser----"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第四階段
			i = phaser.getPhase();
			System.out.println("thread-0 phaser----- "+i);
		}).start();
		new Thread(()->{
			//開始第一階段
			int i = phaser.getPhase();
			System.out.println("thread-1 phaser--"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第二階段
			i = phaser.getPhase();
			System.out.println("thread-1 phaser---"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第三階段
			i = phaser.getPhase();
			System.out.println("thread-1 phaser----"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第四階段
			i = phaser.getPhase();
			System.out.println("thread-1 phaser----- "+i);
		}).start();
		new Thread(()->{
			//開始第一階段
			int i = phaser.getPhase();
			System.out.println("thread-2 phaser--"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第二階段
			i = phaser.getPhase();
			System.out.println("thread-2 phaser---"+i);
			//註銷啦,其實這裏的註銷僅僅是告訴Phaser,註冊的線程少了一個,具體少了哪一個它並不知道
			//phaser.arriveAndDeregister();
			phaser.arriveAndAwaitAdvance();
			//開始第三階段
			i = phaser.getPhase();
			System.out.println("thread-2 phaser----"+i);
			phaser.arriveAndAwaitAdvance();
			//開始第四階段
			i = phaser.getPhase();
			System.out.println("thread-2 phaser----- "+i);
		}).start();
		//運行結果
//		thread-0 phaser--0
//		thread-1 phaser--0
//		thread-2 phaser--0
//		thread-2 phaser---1
//		thread-1 phaser---1
//		thread-0 phaser---1
//		thread-1 phaser----2
//		thread-0 phaser----2
//		thread-2 phaser----2
//		thread-1 phaser----- -2147483645
//		thread-0 phaser----- -2147483645
//		thread-2 phaser----- -2147483645

	}

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