多執行緒工具類:CountDownLatch、CyclicBarrier、Semaphore、LockSupport
CountDownLatch
假如有一個任務想要往下執行,但必須要等到其他的任務執行完畢後才可以。
比如你想要買套房子,但是呢你現在手上沒有錢。你得等這個月工資發了、然後年終獎發了、然後朋友借你得錢還給你了、然後再給朋友借一部分才可以買,這種場景你就可以使用CountDownLatch。
CountDownLatch是JDK為我們提供的一個計數器,它的操作是原子操作,同一時間只能有一個執行緒去操作這個它。
我們先來看一下CountDownLatch的主要方法。
//構造方法,接收計數器的數量 public CountDownLatch(int count) //持續等待計數器歸零 public void await() //最多等待unit時間單位內timeout時間 public boolean await(long timeout, TimeUnit unit) //計數器減1 public void countDown() //返回現在的計數器數量 public long getCount()
下面是CountDownLatch的基本使用示例程式碼:
public class CountDownLatchDemo { public static CountDownLatch countDownLatch = new CountDownLatch(5); static class ThreadDemo extends Thread { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getId() + "完成任務"); countDownLatch.countDown(); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { new ThreadDemo().start(); } countDownLatch.await(); System.out.println("全部完成任務"); } }
CyclicBarrier
相比較於CountDownLatch,CyclicBarrier可以完成前者的全部功能,但是相比前者,它的功能更加的強大。
-
CyclicBarrier翻譯過來的中文名稱叫迴圈柵欄,顧名思義它可以迴圈使用
-
CyclicBarrier還可以接收一個Runnable物件,當柵欄迴圈一次技術後會執行一次Runnable
我們來看一下CyclicBarrier的常用方法:
//構造方法,第一個引數為柵欄餓長度,第二個就是上方所說的Runnable物件 public CyclicBarrier(int parties, Runnable barrierAction) public CyclicBarrier(int parties) //獲取現在的數量 public int getParties() //持續等待柵欄歸零 public int await() //最多等待unit時間單位內timeout時間 public int await(long timeout, TimeUnit unit)
下面是CyclicBarrier的基本使用示例程式碼:
public class CyclicBarrierDemo { public static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new FinallyThreadDemo()); static class ThreadDemo extends Thread { @Override public void run() { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getId() + "完成任務"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("到達屏障點每個執行緒都會瞬時繼續執行"); } } static class FinallyThreadDemo extends Thread { @Override public void run() { System.out.println("所有任務已經完成之後單獨執行的任務!"); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { new ThreadDemo().start(); } } }
觀察列印結果我們可以發現:
當迴圈柵欄的任務執行完一輪以後,如果構造時傳入了Runnable物件,則先執行Runnable物件,然後在瞬間釋放所有任務的鎖。
14完成任務 15完成任務 16完成任務 17完成任務 18完成任務 所有任務已經完成之後單獨執行的任務! 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 19完成任務 20完成任務 21完成任務 22完成任務 23完成任務 所有任務已經完成之後單獨執行的任務! 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行 到達屏障點每個執行緒都會瞬時繼續執行
Semaphore
在淺談Java中的鎖:Synchronized、重入鎖、讀寫鎖 一文中,我們瞭解了synch和讀寫鎖,我們發現使用鎖的時候一次只允許一條執行緒方法。那麼有什麼東西可以提供更強大的控制方法麼?這個東西就是訊號量。
訊號量提供的主要方法:
//建立具有給定許可數的訊號量 Semaphore(int permits):構造方法,建立 //拿走1個許可 void acquire() //拿走多個許可 void acquire(int n) //釋放一個許可 void release() //釋放n個許可 void release(int n): //當前可用的許可數 int availablePermits():
下面來看使用示例:
public class SemaphoreThreadDemo { public static Semaphore semaphore = new Semaphore(5); static class ThreadDemo extends Thread { @Override public void run() { try { semaphore.acquire(); System.out.println(Thread.currentThread().getId() + "號執行緒在"+System.currentTimeMillis()+"獲取資源"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 30; i++) { new ThreadDemo().start(); } } }
LockSupport
我們在詳解Thread多執行緒 和生產者消費者模型 兩篇文章中使用過wait和notify實現了執行緒之間的協作,其實關於執行緒協作JDK還為我們提供了另外一個工具類LockSupport。
使用LockSupport實現等待通知功能時還不需要獲取鎖哦
先來看一下LockSupport的常用方法:
// 禁用當前執行緒 static void park() // 如果引數執行緒的不可用,則使其可用。 static void unpark(Thread thread)
來看一下示例程式碼:
public class LockSupportThreadDemo { public static Thread thread; static class WaitThreadDemo extends Thread { @Override public void run() { System.out.println("WaitThread wait,time=" + System.currentTimeMillis()); thread = Thread.currentThread(); LockSupport.park(); System.out.println("WaitThread end,time=" + System.currentTimeMillis()); } } static class NotifyThreadDemo extends Thread { @Override public void run() { System.out.println("NotifyThread notify,time=" + System.currentTimeMillis()); LockSupport.unpark(thread); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("NotifyThread end,time=" + System.currentTimeMillis()); } } public static void main(String[] args) { WaitThreadDemo waitThreadDemo = new WaitThreadDemo(); NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo(); waitThreadDemo.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } notifyThreadDemo.start(); } }