至關於一扇門,在閉鎖到達結束狀態以前,這扇門是關着的,因此的線程都不容許經過,當閉鎖到達結束狀態,這扇門打開並容許全部的線程經過。在閉鎖達到結束狀態後,將不會再改變狀態,這扇門永遠處於打開狀態。算法
1) 確保某個計算在其全部資源都被初始化以後才繼續執行;多線程
2) 確保某個服務在其全部所依賴的服務都已經啓動後在啓動;jvm
3) 等待某個操做的全部參與者都就緒再繼續執行(例如王者榮耀匹配成功後需等待全部玩家確認後才能進行選英雄)。ide
CountDownLatch是一種靈活的閉鎖實現,上述場景均可以都可使用。該類的簡單用法以下:函數
public class SysUtil { final CountDownLatch startGate; final CountDownLatch endGate; public static void main(String[] args) throws InterruptedException { int num = 20; SysUtil sysUtil = new SysUtil(1, num); for (int i = 0; i < num; i++) { Thread thread = new Thread(sysUtil.new Worker()); thread.start(); } long time = System.currentTimeMillis(); System.out.println("準備好所需的資源"); // doPreparedResource() // open the door sysUtil.startGate.countDown(); try { // 等待全部線程執行完成 sysUtil.endGate.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - time); } public SysUtil(int start, int end) { startGate = new CountDownLatch(start); endGate = new CountDownLatch(end); } class Worker implements Runnable{ private final CountDownLatch startSignal; private final CountDownLatch doSignal; public Worker() { this.startSignal = startGate; this.doSignal = endGate; } @Override public void run() { try { startSignal.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": i through the startGate"); doSignal.countDown(); } } }
上面代碼使用了兩個閉鎖,分別表示「起始門」和「結束門」,起始門初始值設置爲1,結束門設置爲工做線程數量。在資源未完成加載以前(doPreparedResource()),全部線程被拒之起始門外,加載完資源後,大門打開,線程進入辦公樓開始幹活,等到下班時間了,辦公大樓就能夠打開結束門放線程回家。測試
FutureTask實現了Future語義,表示一直抽象的可生成結果的計算。FutureTask表示的計算是經過Callable來實現的。計算結果經過get()方法得到,若是任務已經完成,那麼get方法會當即返回結果,不然get方法將阻塞直到任務進入完成狀態(包括正常結束、因爲取消而結束及因爲異常而結束),而後返回結果或拋出異常。ui
public class FutureTask<V> implements RunnableFuture<V>this
public interface RunnableFuture<V> extends Runnable, Future<V>spa
Runable接口咱們熟悉只有一個run()方法線程
Future接口方法以下:
public class FutureTaskTest { @Test public void test() throws InterruptedException, ExecutionException { FutureTask<Integer> futureTask = new FutureTask<>(new Task()); Thread thread = new Thread(futureTask); long startTime = System.currentTimeMillis(); thread.start(); System.out.println("result: " + futureTask.get()); long endTime = System.currentTimeMillis(); System.out.println("花費時間:" + (endTime - startTime)); } class Task implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("--------線程執行中------------"); int sum = 0; for (int i = 0; i < 100; i++) { Thread.sleep(10); sum += i; } return sum; } } }
計數信號量用來控制同時訪問某個特定資源的操做數量,或者同時執行某個指定操做的數量。另外信號量還能夠用來實現某種資源池,或對容器施加邊界。
Semaphore中管理着一組虛擬許可(permit),許可的初始數量可經過構造函數來指定,在執行操做以前首先得到(acquire)許可,在使用後釋放許可。若是沒有許可,那麼acquire將阻塞直到有permit(或者直到被中斷或操做超時)。Release方法將返回一個許可給信號量。
/* * 利用信號量對set設置邊界 */ public class SemephoreTest<T> { private final Set<T> set; private final Semaphore semaphore; public SemephoreTest(int boundNum) { this.set = Collections.synchronizedSet(new HashSet<>()); // 建立必定數量的許可 this.semaphore = new Semaphore(boundNum); } public boolean add(T o) { boolean wadAdd = false; try { // 請求許可 semaphore.acquire(); wadAdd = set.add(o); return wadAdd; } catch (InterruptedException e) { e.printStackTrace(); }finally { // 元素添加失敗,釋放許可 if (!wadAdd) { semaphore.release(); } } return wadAdd; } public boolean remove(T o) { boolean wasRemoved = set.remove(o); if (wasRemoved) { semaphore.release(); } return wasRemoved; } }
柵欄相似於閉鎖,它能阻塞一組線程直到某個事件發生。柵欄和閉鎖的區別在於,全部線程必須都到達柵欄位置了,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其餘線程。
CyclicBarrier可使必定的數量的參與方反覆地在柵欄處聚集,在並行迭代算法中很是有用。但線程到達柵欄處時將調用柵欄的await方法,該方法會阻塞當前線程直到全部的線程都到達了柵欄處。若是全部的線程都到達了柵欄處,柵欄將會打開,此時全部的線程都會被釋放,而柵欄將被重置以便下次使用(閉鎖不可被重置)。若是await調用超時或者阻塞的線程被中斷,全部的await調用都終止並拋出BrokenBarrierException。若是成功經過柵欄,await將爲每一個線程返回惟一的到達索引號。
public class BarrierTest { private final CyclicBarrier cyclicBarrier; private Worker[] workers; public BarrierTest() { int count = Runtime.getRuntime().availableProcessors() * 2; count = 10; cyclicBarrier = new CyclicBarrier(count, new Runnable() { @Override public void run() { System.out.println("open the barrier"); } }); this.workers = new Worker[count]; for (int i = 0; i < workers.length; i++) { workers[i] = new Worker(); } } class Worker implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName() + ":我到了,等人齊"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "衝啊"); } } public void start() { for (int i = 0; i < workers.length; i++) { new Thread(workers[i]).start(); } } // 不可以使用Junit進行多線程的測試,Junit中是將當前 test做爲參數,放入到 Junit TestRunner中的main函數 // 做爲一個子線程運行,當測試線程運行成功,將把jvm中止。具體看下面代碼 /* public static void main(String args[]) { TestRunner aTestRunner = new TestRunner(); try { TestResult r = aTestRunner.start(args); if (!r.wasSuccessful()) System.exit(FAILURE_EXIT); System.exit(SUCCESS_EXIT); } catch (Exception e) { System.err.println(e.getMessage()); System.exit(EXCEPTION_EXIT); } } */ @Test public void test() { new BarrierTest().start(); } public static void main(String[] args) { new BarrierTest().start(); } }
Exchanger類可用於兩個線程之間交換信息,是一種兩方柵欄。可簡單地將Exchanger對象理解爲一個包含兩個格子的容器,經過exchanger方法能夠向兩個格子中填充信息。當兩個格子中的均被填充時,該對象會自動將兩個格子的信息交換,而後返回給線程,從而實現兩個線程的信息交換。
另外須要注意的是,Exchanger類僅可用做兩個線程的信息交換,當超過兩個線程調用同一個exchanger對象時,獲得的結果是隨機的,未獲得配對的線程,則會被阻塞,永久等待,直到與之配對的線程到達位置,exchanger對象僅關心其包含的兩個「格子」是否已被填充數據,當兩個格子都填充數據完成時,該對象就認爲線程之間已經配對成功,而後開始執行數據交換操做。
public class ExchangerTest { public static void main(String[] args) { Exchanger<String> barrier = new Exchanger<>(); Comsumer comsumer = new ExchangerTest(). new Comsumer(barrier); Producer prodecer = new ExchangerTest(). new Producer(barrier); Thread thread1 = new Thread(comsumer); Thread thread2 = new Thread(prodecer); thread1.start(); thread2.start(); } class Comsumer implements Runnable{ private Exchanger<String> changer; public Comsumer(Exchanger<String> exchanger) { this.changer = exchanger; } @Override public void run() { String changes = null; try { changes = changer.exchange("money"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Consumer recieve:" + changes); } } class Producer implements Runnable{ private Exchanger<String> changer; public Producer(Exchanger<String> exchanger) { this.changer = exchanger; } @Override public void run() { String changes = null; try { changes = changer.exchange("products"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Producer recieve:" + changes); } } }