CountDownLatch ,CyclicBarrier 和 Semphore

閉鎖 CountDownLatch 和 循環柵欄 CyclicBarrier 都是同步工具類。

 

所有的同步工具類包含一些特定的結構化屬性: 它們封裝了一些狀態,這些狀態將決定執行同步工具類的線程是繼續執行還是等待,此外還提供了一些方法對狀態進行操作,以及另一些方法用於高效地等待同步工具類進入到預期狀態。

 

一、閉鎖 CountDownLatch 

CountDownLatch 是一種靈活的閉鎖實現,它可以使一個或者多個線程等待一組事件發生;

 

閉鎖狀態包括一個計數器,該計數器被初始化爲一個正數,表示需要等待的事件數量。countDown 方法遞減計數器,表示有一個事件已經發生了,而 await 方法等待計數器達到零,這表示所有需要等待的事件都已經發生。如果計數器的值非零,那麼 await 會一直阻塞直到計數器爲零,或者等待中的線程中斷,或者等待超時;

 

CountDownlatch 允許線程等待直到計數器減爲 0;

 

使用場合: 當一個或多個線程需要等待直到指定數目的事件發生。

 

例子:

 

public class CountDownLatchTest {
	private static int LATCH_SIZE = 5;
	
	public static void main(String[] args) {
		
		try {
			CountDownLatch latch = new CountDownLatch(LATCH_SIZE);
			
			for(int i = 0; i < LATCH_SIZE; i++){
				new WorkerThread(latch).start();
			}
			
			System.out.println("主線程等待.");
			
			latch.await();
			
			System.out.println("主線程繼續執行");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
	static class WorkerThread extends Thread{
		CountDownLatch mLatch;
		
		public WorkerThread(CountDownLatch latch){
			mLatch = latch;
		}
		
		@Override
		public void run() {
			super.run();
			
			try {
				Thread.sleep(2000);
				System.out.println(Thread.currentThread().getName() + " 執行操作.");
				// 將 CountDownLatch 的數量減 1
				mLatch.countDown();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

 

 

運行結果:

 

 

二、樟柵 CyclicBarrier

  CyclicBarrier 類實現了一個集結點(rendezvous) 稱爲樟柵(barrier)。 考慮大量線程運算在一次計算的不同部分的情形。當所有部分都 準備好時,需要把結果組合在一起。當一個線程完成了它的那一部分後,讓它運行到樟柵處。一旦所有的線程都到達了這個樟柵,樟柵就撤銷, 線程就可以繼續運行了。

 

如果任何一個在樟柵上等待的線程離開了樟柵,那麼樟柵就被破壞了(線程可能離開是因爲它調用 await 時設置了超時,或者因爲它被中斷 了)。在這種情況下,所有其他線程的 await 方法拋出 BrokenBarrierExecption 異常。那些已經在等待的線程立即終止 await 的調用。

 

可以提供一個可選的樟柵動作(barrier action), 當所有線程到達樟柵的時候就會執行這一動作。

Runnable barrierAction = ...;

CyclicBarrier barrier = new CyclicBarrier(nThreads, barrierAction);

 

樟柵被稱爲是循環的(cyclic),因爲可以在所有等待線程被釋放後被重用。這點,有別於 CountDownLatch,CountDownLatch 只能被使用一 次。

 

例子:

 

public class CyclicBarrierTest {
	private static final int SIZE = 5;
	private static CyclicBarrier mCyclicBarrier;
	
	public static void main(String[] args) {
		mCyclicBarrier = new CyclicBarrier(SIZE, new Runnable() {
			@Override
			public void run() {
				// 當所有線程到達 barrier(樟柵) 的時候就會執行 barrier action.
				System.out.println("---> 滿足條件,執行特定操作");
			}
		});
		
		// 創建 5 個任務
		for(int i = 0; i < SIZE; i++){
			new WorkerThread().start();
		}
		
	}
	
	static class WorkerThread extends Thread{
		@Override
		public void run() {
			super.run();
			
			try {
				System.out.println(Thread.currentThread().getName() + " 等待 CyclicBarrier.");
				// 將 mCyclicBarrier 的參與者數量加 1
				mCyclicBarrier.await();
				// mCyclicBarrier 的參與者數量等於 5 時,才繼續往後執行
				System.out.println(Thread.currentThread().getName() + " 繼續執行.");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

 

 

 

運行結果:

三、Semphore

管理一組許可,許可通過構造函數來指定。在操作前通過 acquire 阻塞直到有許可,才能操作,在操作完成後,通過 release() 許可

 

public class SemaphoreTest {
    private static final String TAG = "SemaphoreTest";
    private static final int THREAD_SIZE = 5;
    private Semaphore mSemaphore;


    public SemaphoreTest() {
        mSemaphore = new Semaphore(1);
        startThread();
    }

    private void startThread(){
        for (int i = 0; i < THREAD_SIZE; i++){
            new PrinterThread().start();
        }
    }

    private class PrinterThread extends Thread {
        @Override
        public void run() {
            try {
                mSemaphore.acquire();
                Log.i(TAG, Thread.currentThread().getName() + "進入打印");
                Thread.sleep(1000);
                Log.i(TAG,Thread.currentThread().getName() + "打印中...");
                Log.i(TAG,Thread.currentThread().getName() + "退出打印");
                mSemaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

打印的結果 https://blog.csdn.net/qq_30379689/article/details/53769474

 

例子來源https://blog.csdn.net/qq_30379689/article/details/53769474

 

四、CountDownLatch 和 CyclicBarrier 的區別

1、樟柵與閉鎖的關鍵區別在於,所有線程必須同時達到樟柵位置,才能繼續執行。閉鎖用於等待事件,而樟柵用於等待其他線 程;

2、CountDownLatch 的作用是允許1個或者 N 個線程等待其他線程完成執行,而 CyclicBarrier 則是允許 N 個線程相互等待;

3、CountDownLatch 的計數器無法被重置,CyclicBarrier 的計數器可以被重置後使用,因此,它被稱爲是循環的 barrier.

 

五、說明:

1、SharedPreferences 源碼中 apply 方法的 awaitCommit 任務中就使用了 CountDownLatch;

2、這裏是《Java 核心技術卷I》第14章 P688, 《Java 併發編程》第五章 P83, 《Android 開發進階 從小工到專家》 第3章 P94   的內容的綜合。

 

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