正文前先來一波福利推薦:
福利一:
百萬年薪架構師視頻,該視頻可以學到很多東西,是本人花錢買的VIP課程,學習消化了一年,為了支持一下女朋友公眾號也方便大家學習,共享給大家。
福利二:
畢業答辯以及工作上各種答辯,平時積累了不少精品PPT,現在共享給大家,大大小小加起來有幾千套,總有適合你的一款,很多是網上是下載不到。
獲取方式:
微信關注 精品3分鍾 ,id為 jingpin3mins,關注后回復 百萬年薪架構師 ,精品收藏PPT 獲取雲盤鏈接,謝謝大家支持!

-----------------------正文開始---------------------------
1、java.util.concurrent 包下的類分類圖

- locks部分:顯式鎖(互斥鎖和速寫鎖)相關;
- atomic部分:原子變量類相關,是構建非阻塞算法的基礎;
- executor部分:線程池相關;
- collections部分:並發容器相關;
- tools部分:同步工具相關,如信號量、閉鎖、柵欄等功能;
1、1 collections部分:
1、1、1 BlockingQueue
BlockingQueue為接口,如果要是他,需要使用實現他的子類;
BlockingQueue的子類包括'
ArrayBlockingQueue;
DelayQueue;
LinkedBlockingQueue;
SynchronousQueue;
PriorirtyBlockingQueue;
TransferQueue;
BlockingQueue 用法
BlockingQueue 通常用於一個線程生產對象,而另外一個線程消費這些對象的場景。下圖是對這個原理的闡述:
一個線程往里邊放,另外一個線程從里邊取的一個 BlockingQueue。
一個線程將會持續生產新對象並將其插入到隊列之中,直到隊列達到它所能容納的臨界點。也就是說,它是有限的。如果該阻塞隊列到達了其臨界點,負責生產的線程將會在往里邊插入新對象時發生阻塞。它會一直處於阻塞之中,直到負責消費的線程從隊列中拿走一個對象。負責消費的線程將會一直從該阻塞隊列中拿出對象。如果消費線程嘗試去從一個空的隊列中提取對象的話,這個消費線程將會處於阻塞之中,直到一個生產線程把一個對象丟進隊列。
BlockingQueue 的方法
BlockingQueue 具有 4 組不同的方法用於插入、移除以及對隊列中的元素進行檢查。如果請求的操作不能得到立即執行的話,每個方法的表現也不同。這些方法如下:
四組不同的行為方式解釋:
拋異常:如果試圖的操作無法立即執行,拋一個異常。
特定值:如果試圖的操作無法立即執行,返回一個特定的值(常常是 true / false)。
阻塞:如果試圖的操作無法立即執行,該方法調用將會發生阻塞,直到能夠執行。
超時:如果試圖的操作無法立即執行,該方法調用將會發生阻塞,直到能夠執行,但等待時間不會超過給定值。返回一個特定值以告知該操作是否成功(典型的是 true / false)。
無法向一個 BlockingQueue 中插入 null。如果你試圖插入 null,BlockingQueue 將會拋出一個 NullPointerException。
可以訪問到 BlockingQueue 中的所有元素,而不僅僅是開始和結束的元素。比如說,你將一個對象放入隊列之中以等待處理,但你的應用想要將其取消掉。那么你可以調用諸如 remove(o) 方法來將隊列之中的特定對象進行移除。但是這么干效率並不高(譯者注:基於隊列的數據結構,獲取除開始或結束位置的其他對象的效率不會太高),因此你盡量不要用這一類的方法,除非你確實不得不那么做。
Java 中使用 BlockingQueue 的例子
這里是一個 Java 中使用 BlockingQueue 的示例。本示例使用的是 BlockingQueue 接口的 ArrayBlockingQueue 實現。
首先,BlockingQueueExample 類分別在兩個獨立的線程中啟動了一個 Producer 和 一個 Consumer。
Producer 向一個共享的 BlockingQueue 中注入字符串,而 Consumer 則會從中把它們拿出來。
public class BlockingQueueExample { public static void main(String[] args) throws Exception { BlockingQueue queue = new ArrayBlockingQueue(1024); Producer producer = new Producer(queue); Consumer consumer = new Consumer(queue); new Thread(producer).start(); new Thread(consumer).start(); Thread.sleep(4000); } }
以下是 Producer 類。注意它在每次 put() 調用時是如何休眠一秒鍾的。這將導致 Consumer 在等待隊列中對象的時候發生阻塞。
public class Producer implements Runnable{ protected BlockingQueue queue = null; public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { try { queue.put("1"); //無需考慮安全問題 直接使用 Thread.sleep(1000); queue.put("2"); Thread.sleep(1000); queue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } } }
以下是 Consumer 類。它只是把對象從隊列中抽取出來,然后將它們打印到 System.out。
public class Consumer implements Runnable{ protected BlockingQueue queue = null; public Consumer(BlockingQueue queue) { this.queue = queue; } public void run() { try { System.out.println(queue.take()); System.out.println(queue.take()); System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }
數組阻塞隊列 ArrayBlockingQueue
ArrayBlockingQueue 類實現了 BlockingQueue 接口。
ArrayBlockingQueue 是一個有界的阻塞隊列,其內部實現是將對象放到一個數組里。有界也就意味着,它不能夠存儲無限多數量的元素。它有一個同一時間能夠存儲元素數量的上限。
你可以在對其初始化的時候設定這個上限,但之后就無法對這個上限進行修改了(譯者注:因為它是基於數組實現的,也就具有數組的特性:一旦初始化,大小就無法修改)。
‘ArrayBlockingQueue 內部以 FIFO(先進先出)的順序對元素進行存儲。隊列中的頭元素在所有元素之中是放入時間最久的那個,而尾元素則是最短的那個。
ArrayBlockingQueue的使用代碼案例見上例代碼。
4. 延遲隊列 DelayQueue
DelayQueue 實現了 BlockingQueue 接口。DelayQueue 對元素進行持有直到一個特定的延遲到期。注入其中的元素必須實現 java.util.concurrent.Delayed 接口,該接口定義:
public interface Delayed extends Comparable<Delayed< { public long getDelay(TimeUnit timeUnit); //注意此處的TimeUnit的使用 }
DelayQueue 將會在每個元素的 getDelay() 方法返回的值的時間段之后才釋放掉該元素。如果返回的是 0 或者負值,延遲將被認為過期,該元素將會在 DelayQueue 的下一次 take 被調用的時候被釋放掉。
也就是說通過getDelay返回元素到期時間,只有元素在隊列中存在的時間超過該時間后,才可以在延遲隊列取出該對象;
傳遞給 getDelay 方法的 getDelay 實例是一個枚舉類型,它表明了將要延遲的時間段。
TimeUnit 枚舉將會取以下值:
DAYS
HOURS
MINUTES
SECONDS
MILLISECONDS
MICROSECONDS
NANOSECONDS
正如你所看到的,Delayed 接口也繼承了 java.lang.Comparable 接口,這也就意味着 Delayed 對象之間可以進行對比。這個在對 DelayQueue 隊列中的元素進行添加時排序,因此它們可以根據過期時間進行有序釋放。以下是使用 DelayQueue 的例子:
DelayQueue的兩個應用案例:【注意該案例中用到了 concrrent 包下的 tools 下的 countDownLatch 如果對該部分不理解 先看 1.2 部門的 Tools 】
1、2 Tools部分:
CountDownLatch用法
CountDownLatch類位於java.util.concurrent包下,利用它可以實現類似計數器的功能。比如有一個任務A,它要等待其他4個任務執行完畢之后才能執行,此時就可以利用CountDownLatch來實現這種功能了。
通俗一點的說 就是 使用CountDownLatch 時 幾個線程使用同一個CountDownLatch對象 有一個線程調用wait方法 其他線程使用countDown()方法,當最后計數器減到0的時候,wait調用處 繼續往下執行;
CountDownLatch類只提供了一個構造器:
public CountDownLatch(int count) { }; //參數count為計數值
然后下面這3個方法是CountDownLatch類中最重要的方法:
public void await() throws InterruptedException { }; //調用await()方法的線程會被掛起,它會等待直到count值為0才繼續執行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()類似,只不過等待一定的時間后count值還沒變為0的話就會繼續執行 public void countDown() { }; //將count值減1
下面看使用范例:
public class Test { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread(){ public void run() { try { System.out.println("子線程"+Thread.currentThread().getName()+"正在執行"); Thread.sleep(3000); System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread(){ public void run() { try { System.out.println("子線程"+Thread.currentThread().getName()+"正在執行"); Thread.sleep(3000); System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2個子線程執行完畢..."); latch.await(); System.out.println("2個子線程已經執行完畢"); System.out.println("繼續執行主線程"); } catch (InterruptedException e) { e.printStackTrace(); } } }
執行結果;
線程Thread-0正在執行 線程Thread-1正在執行 等待2個子線程執行完畢... 線程Thread-0執行完畢 線程Thread-1執行完畢 2個子線程已經執行完畢 繼續執行主線程
CyclicBarrier用法
字面意思回環柵欄,通過它可以實現讓一組線程等待至某個狀態之后再全部同時執行。叫做回環是因為當所有等待線程都被釋放以后,CyclicBarrier可以被重用。我們暫且把這個狀態就叫做barrier,當調用await()方法之后,線程就處於barrier了。
通俗的講 就是 使用 CyclicBrarrier 幾個線程在執行到需要進行柵欄的地方時 調用 CyclicBrarrier 對象的 wait 方法 然后當所有的對象都執行到柵欄的地方后 統一進行放行 往后執行;
CyclicBarrier類位於java.util.concurrent包下,CyclicBarrier提供2個構造器:
public CyclicBarrier(int parties, Runnable barrierAction) { } public CyclicBarrier(int parties) { }
參數parties指讓多少個線程或者任務等待至barrier狀態;
參數barrierAction為當這些線程都達到barrier狀態時會執行的內容;其實就是 當所有線程到達柵欄后,如果需要執行在放行前需要執行一個其他操作,可以實現runnable的run方法,此時會調用一個線程執行run方法;
然后CyclicBarrier中最重要的方法就是await方法,它有2個重載版本:
public int await() throws InterruptedException, BrokenBarrierException { }; public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
第一個版本比較常用,用來掛起當前線程,直至所有線程都到達barrier狀態再同時執行后續任務;
第二個版本是讓這些線程等待至一定的時間,如果還有線程沒有到達barrier狀態就直接讓到達barrier的線程執行后續任務。
下面舉幾個例子就明白了:
假若有若干個線程都要進行寫數據操作,並且只有所有線程都完成寫數據操作之后,這些線程才能繼續做后面的事情,此時就可以利用CyclicBarrier了:
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) new Writer(barrier).start(); } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據..."); try { Thread.sleep(5000); //以睡眠來模擬寫入數據操作 System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢"); cyclicBarrier.await(); //柵欄的地方 所有線程停留在此處等待其他線程執行到此 } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println("所有線程寫入完畢,繼續處理其他任務..."); } } }
測試結果:
線程Thread-0正在寫入數據... 線程Thread-3正在寫入數據... 線程Thread-2正在寫入數據... 線程Thread-1正在寫入數據... 線程Thread-2寫入數據完畢,等待其他線程寫入完畢 線程Thread-0寫入數據完畢,等待其他線程寫入完畢 線程Thread-3寫入數據完畢,等待其他線程寫入完畢 線程Thread-1寫入數據完畢,等待其他線程寫入完畢 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務...
從上面輸出結果可以看出,每個寫入線程執行完寫數據操作之后,就在等待其他線程寫入操作完畢。
當所有線程線程寫入操作完畢之后,所有線程就繼續進行后續的操作了。
如果說想在所有線程寫入操作完之后,進行額外的其他操作可以為CyclicBarrier提供Runnable參數:
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N,new Runnable() { @Override public void run() { System.out.println("當前線程"+Thread.currentThread().getName()); } }); for(int i=0;i<N;i++) new Writer(barrier).start(); } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據..."); try { Thread.sleep(5000); //以睡眠來模擬寫入數據操作 System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println("所有線程寫入完畢,繼續處理其他任務..."); } } }
測試結果:
線程Thread-0正在寫入數據... 線程Thread-1正在寫入數據... 線程Thread-2正在寫入數據... 線程Thread-3正在寫入數據... 線程Thread-0寫入數據完畢,等待其他線程寫入完畢 線程Thread-1寫入數據完畢,等待其他線程寫入完畢 線程Thread-2寫入數據完畢,等待其他線程寫入完畢 線程Thread-3寫入數據完畢,等待其他線程寫入完畢 當前線程Thread-3 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務... 所有線程寫入完畢,繼續處理其他任務...
當柵欄處的wait方法使用帶有設定時間參數的時候,當等待Barrier過程中 超過時間后 就拋出異常並繼續執行后面的任務。
案例代碼:
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) { if(i<N-1) new Writer(barrier).start(); else { //最后一個線程等待5000毫秒后再執行,使得其他線程會發生超時 拋出異常 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } new Writer(barrier).start(); } } } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據..."); try { Thread.sleep(5000); //以睡眠來模擬寫入數據操作 System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢"); try { cyclicBarrier.await(2000, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"所有線程寫入完畢,繼續處理其他任務..."); } } }
線程Thread-0正在寫入數據... 線程Thread-2正在寫入數據... 線程Thread-1正在寫入數據... 線程Thread-2寫入數據完畢,等待其他線程寫入完畢 線程Thread-0寫入數據完畢,等待其他線程寫入完畢 線程Thread-1寫入數據完畢,等待其他線程寫入完畢 線程Thread-3正在寫入數據... java.util.concurrent.TimeoutException Thread-1所有線程寫入完畢,繼續處理其他任務... Thread-0所有線程寫入完畢,繼續處理其他任務... at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) java.util.concurrent.BrokenBarrierException at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) java.util.concurrent.BrokenBarrierException at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) Thread-2所有線程寫入完畢,繼續處理其他任務... java.util.concurrent.BrokenBarrierException 線程Thread-3寫入數據完畢,等待其他線程寫入完畢 at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) Thread-3所有線程寫入完畢,繼續處理其他任務...
另外CyclicBarrier是可以重用的,看下面這個例子:
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) { new Writer(barrier).start(); } try { Thread.sleep(25000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("CyclicBarrier重用"); for(int i=0;i<N;i++) { new Writer(barrier).start(); } } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據..."); try { Thread.sleep(5000); //以睡眠來模擬寫入數據操作 System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"所有線程寫入完畢,繼續處理其他任務..."); } } }
測試結果:
線程Thread-0正在寫入數據... 線程Thread-1正在寫入數據... 線程Thread-3正在寫入數據... 線程Thread-2正在寫入數據... 線程Thread-1寫入數據完畢,等待其他線程寫入完畢 線程Thread-3寫入數據完畢,等待其他線程寫入完畢 線程Thread-2寫入數據完畢,等待其他線程寫入完畢 線程Thread-0寫入數據完畢,等待其他線程寫入完畢 Thread-0所有線程寫入完畢,繼續處理其他任務... Thread-3所有線程寫入完畢,繼續處理其他任務... Thread-1所有線程寫入完畢,繼續處理其他任務... Thread-2所有線程寫入完畢,繼續處理其他任務... CyclicBarrier重用 線程Thread-4正在寫入數據... 線程Thread-5正在寫入數據... 線程Thread-6正在寫入數據... 線程Thread-7正在寫入數據... 線程Thread-7寫入數據完畢,等待其他線程寫入完畢 線程Thread-5寫入數據完畢,等待其他線程寫入完畢 線程Thread-6寫入數據完畢,等待其他線程寫入完畢 線程Thread-4寫入數據完畢,等待其他線程寫入完畢 Thread-4所有線程寫入完畢,繼續處理其他任務... Thread-5所有線程寫入完畢,繼續處理其他任務... Thread-6所有線程寫入完畢,繼續處理其他任務... Thread-7所有線程寫入完畢,繼續處理其他任務...
Semaphore用法
Semaphore翻譯成字面意思為 信號量,Semaphore可以控同時訪問的線程個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。
Semaphore類位於java.util.concurrent包下,它提供了2個構造器:
public Semaphore(int permits) { //參數permits表示許可數目,即同時可以允許多少線程進行訪問 sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { //這個多了一個參數fair表示是否是公平的,即等待時間越久的越先獲取許可 sync = (fair)? new FairSync(permits) : new NonfairSync(permits); }
下面說一下Semaphore類中比較重要的幾個方法,首先是acquire()、release()方法:
public void acquire() throws InterruptedException { } //獲取一個許可 public void acquire(int permits) throws InterruptedException { } //獲取permits個許可 public void release() { } //釋放一個許可 public void release(int permits) { } //釋放permits個許可
acquire()用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許可。
release()用來釋放許可。注意,在釋放許可之前,必須先獲獲得許可。
這4個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法
public boolean tryAcquire() { }; //嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false public boolean tryAcquire(int permits) { }; //嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false
下面通過一個例子來看一下Semaphore的具體使用:
假若一個工廠有5台機器,但是有8個工人,一台機器同時只能被一個工人使用,只有使用完了,其他工人才能繼續使用。那么我們就可以通過Semaphore來實現:【acquire 是阻塞式等待獲得執行許可】
public class Test { public static void main(String[] args) { int N = 8; //工人數 Semaphore semaphore = new Semaphore(5); //機器數目 for(int i=0;i<N;i++) new Worker(i,semaphore).start(); } static class Worker extends Thread{ private int num; private Semaphore semaphore; public Worker(int num,Semaphore semaphore){ this.num = num; this.semaphore = semaphore; } @Override public void run() { try { semaphore.acquire(); //如果新線程進入后不能夠獲得許可 則會阻塞 直到獲得許可然后再往下執行
//獲得到使用資源的能力 並發執行的線程數 取決於 semaphore 的個數配置 此配置數也就是 可用資源的數目
System.out.println("工人"+this.num+"占用一個機器在生產..."); Thread.sleep(2000); System.out.println("工人"+this.num+"釋放出機器"); semaphore.release(); //釋放許可 } catch (InterruptedException e) { e.printStackTrace(); } } } }
測試結果:
工人0占用一個機器在生產... 工人1占用一個機器在生產... 工人2占用一個機器在生產... 工人4占用一個機器在生產... 工人5占用一個機器在生產...
工人0釋放出機器 工人2釋放出機器
工人3占用一個機器在生產... 工人7占用一個機器在生產...
工人4釋放出機器
工人5釋放出機器
工人1釋放出機器
工人6占用一個機器在生產...
工人3釋放出機器
工人7釋放出機器
工人6釋放出機器
exchanger 用法
Exchanger是在兩個任務之間交換對象的柵欄,當這些任務進入柵欄時,它們各自擁有一個對象。當他們離開時,它們都擁有之前由對象持有的對象。
它典型的應用場景是:一個任務在創建對象,這些對象的生產代價很高昂,而另一個任務在消費這些對象。通過這種方式,可以有更多的對象在被創建的同時被消費。
public class ExchangerTest { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); final Exchanger exchanger = new Exchanger(); executor.execute(new Runnable() { String data1 = "Ling"; @Override public void run() { doExchangeWork(data1, exchanger); } }); executor.execute(new Runnable() { String data2 = "huhx"; @Override public void run() { doExchangeWork(data2, exchanger); } }); executor.shutdown(); //把線程池停掉 釋放資源 } private static void doExchangeWork(String data1, Exchanger exchanger) { try { System.out.println(Thread.currentThread().getName() + "正在把數據 " + data1 + " 交換出去"); Thread.sleep((long) (Math.random() * 1000)); String data2 = (String) exchanger.exchange(data1); System.out.println(Thread.currentThread().getName() + "交換數據 到 " + data2); } catch (InterruptedException e) { e.printStackTrace(); } } }
Tools 總結:
下面對上面說的三個輔助類進行一個總結:
1)CountDownLatch和CyclicBarrier都能夠實現線程之間的等待,只不過它們側重點不同:
CountDownLatch一般用於某個線程A等待若干個其他線程執行完任務之后,它才執行;
而CyclicBarrier一般用於一組線程互相等待至某個狀態,然后這一組線程再同時執行;
另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。
2)Semaphore其實和鎖有點類似,它一般用於控制對某組資源的訪問權限。


