Java併發包——線程通訊
摘要:本文主要學習了Java併發包裏有關線程通訊的一些知識。html
部份內容來自如下博客:java
https://www.cnblogs.com/skywang12345/p/3496716.html多線程
線程通訊方式
對於線程之間的通訊方式,咱們以前使用Object.wait()和Object.notify(),經過與synchronized關鍵字進行同步,二者配合使用,能夠實現線程之間的通訊。併發
後來在JUC併發包裏發現可使用Lock取代synchronized關鍵字實現線程之間的同步,而且使用Lock的方式有比synchronized方式更增強大的功能,爲了同Lock配合,實現線程之間的通訊,就要用到Condition。ide
Condition的做用是對鎖進行更精確的控制。Condition中的await()方法至關於Object的wait()方法,Condition中的signal()方法至關於Object的notify()方法,Condition中的signalAll()至關於Object的notifyAll()方法。不一樣的是,Object中的wait()、notify()、notifyAll()方法是和「同步鎖」(synchronized關鍵字)捆綁使用的,而Condition是須要與「獨享鎖/共享鎖」捆綁使用的。學習
使用Condition的優點
可以更加精細的控制多線程的休眠與喚醒。對於同一個鎖,咱們能夠建立多個Condition,在不一樣的狀況下使用不一樣的Condition。spa
可以在多個線程之間進行通訊。Object的wait()、notify()、notifyAll()方法只能實現兩個線程之間的通訊,而Lock對象能經過newCondition()方法建立出無數的「條件」,經過這些條件,咱們就可以成功地實現多線程之間的數據通訊,對它們進行控制。線程
Condition
Condition是java.util.concurrent.locks包下的一個接口,提供了用來進行線程通訊的方法。code
1 public interface Condition { 2 // 使當前線程加入等待隊列中並釋放當鎖,被通知、被中斷時喚醒。 3 void await() throws InterruptedException; 4 5 // 同await()相似,只是該方法對中斷不敏感,只有被通知時才被喚醒。 6 void awaitUninterruptibly(); 7 8 // 同await()相似,若是在指定時間以內沒有被通知或者被中斷,該方法會返回false。 9 boolean await(long time, TimeUnit unit) throws InterruptedException; 10 11 // 當前線程進入等待狀態,被通知、中斷或者超時以後被喚醒。返回值就是表示剩餘的時間,超時返回值是0或者負數。 12 long awaitNanos(long nanosTimeout) throws InterruptedException; 13 14 // 同awaitNanos(long nanosTimeout)相似,只是參數變成了指定日期。 15 boolean awaitUntil(Date deadline) throws InterruptedException; 16 17 // 喚醒一個在等待隊列中的線程。 18 void signal(); 19 20 // 喚醒全部在等待隊列中的線程。 21 void signalAll(); 22 }
獲取Lock上的特定Condition
Condition實例實質上被綁定到一個鎖上。一個鎖內部能夠有多個Condition,即有多路等待和通知。要爲特定Lock實例得到Condition實例,請使用Lock的newCondition()方法。htm
newCondition()返回用來與當前Lock實例一塊兒使用的Condition實例。
相似於Object.wait()和Object.notify()的功能,Object.wait()與Object.notify()須要結合synchronized使用。Condition須要結合ReentrantLock使用。
使用Condition實現線程通訊
使用synchronized和Object類的方法
使用synchronized和Object類的方法實現兩個線程交替打印,代碼以下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(demoThread, "線程A"); 5 Thread b = new Thread(demoThread, "線程B"); 6 a.start(); 7 b.start(); 8 } 9 } 10 11 class DemoThread implements Runnable { 12 private Integer num = 1; 13 14 @Override 15 public void run() { 16 synchronized (DemoThread.class) { 17 while (num <= 10) { 18 DemoThread.class.notify(); 19 System.out.println(Thread.currentThread().getName() + " >>> " + num++); 20 if (num <= 10) { 21 try { 22 DemoThread.class.wait(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 } 28 } 29 } 30 }
運行結果以下:
1 線程A >>> 1 2 線程B >>> 2 3 線程A >>> 3 4 線程B >>> 4 5 線程A >>> 5 6 線程B >>> 6 7 線程A >>> 7 8 線程B >>> 8 9 線程A >>> 9 10 線程B >>> 10
使用Lock和Condition類的方法
使用Lock和Condition類的方法實現兩個線程交替打印,代碼以下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(demoThread, "線程A"); 5 Thread b = new Thread(demoThread, "線程B"); 6 a.start(); 7 b.start(); 8 } 9 } 10 11 class DemoThread implements Runnable { 12 private Integer num = 1; 13 Lock lock = new ReentrantLock(); 14 Condition condition = lock.newCondition(); 15 16 @Override 17 public void run() { 18 lock.lock(); 19 try { 20 while (num <= 10) { 21 condition.signal(); 22 System.out.println(Thread.currentThread().getName() + " >>> " + num++); 23 if (num <= 10) { 24 condition.await(); 25 } 26 } 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } finally { 30 lock.unlock(); 31 } 32 } 33 }
運行結果以下:
1 線程A >>> 1 2 線程B >>> 2 3 線程A >>> 3 4 線程B >>> 4 5 線程A >>> 5 6 線程B >>> 6 7 線程A >>> 7 8 線程B >>> 8 9 線程A >>> 9 10 線程B >>> 10
使用Lock和Condition類的方法實現三個線程按順序循環打印
使用Lock和Condition類的方法實現三個線程按順序打印,須要喚醒指定的線程,代碼以下:
1 public class Demo { 2 public static void main(String[] args) { 3 DemoThread demoThread = new DemoThread(); 4 Thread a = new Thread(() -> demoThread.run1(), "線程1"); 5 Thread b = new Thread(() -> demoThread.run2(), "線程2"); 6 Thread c = new Thread(() -> demoThread.run3(), "線程3"); 7 a.start(); 8 b.start(); 9 c.start(); 10 } 11 } 12 13 class DemoThread { 14 static private Integer num = 0; 15 static Lock lock = new ReentrantLock(); 16 Condition c1 = lock.newCondition(); 17 Condition c2 = lock.newCondition(); 18 Condition c3 = lock.newCondition(); 19 20 public void run1() { 21 try { 22 Thread.sleep(10); 23 } catch (InterruptedException e1) { 24 e1.printStackTrace(); 25 } 26 lock.lock(); 27 try { 28 while (num < 10) { 29 for (int i = 0; i < 1 && num < 10; i++) { 30 System.out.println(Thread.currentThread().getName() + " >>> " + num); 31 } 32 num++; 33 c2.signal(); 34 c1.await(); 35 if (num >= 9) { 36 c3.signal(); 37 } 38 } 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } finally { 42 lock.unlock(); 43 } 44 } 45 46 public void run2() { 47 try { 48 Thread.sleep(20); 49 } catch (InterruptedException e1) { 50 e1.printStackTrace(); 51 } 52 lock.lock(); 53 try { 54 while (num < 10) { 55 for (int i = 0; i < 2 && num < 10; i++) { 56 System.out.println(Thread.currentThread().getName() + " >>> " + num); 57 } 58 num++; 59 c3.signal(); 60 c2.await(); 61 if (num >= 9) { 62 c1.signal(); 63 } 64 } 65 } catch (Exception e) { 66 e.printStackTrace(); 67 } finally { 68 lock.unlock(); 69 } 70 } 71 72 public void run3() { 73 try { 74 Thread.sleep(30); 75 } catch (InterruptedException e1) { 76 e1.printStackTrace(); 77 } 78 lock.lock(); 79 try { 80 while (num < 10) { 81 for (int i = 0; i < 3 && num < 10; i++) { 82 System.out.println(Thread.currentThread().getName() + " >>> " + num); 83 } 84 num++; 85 c1.signal(); 86 c3.await(); 87 if (num >= 9) { 88 c2.signal(); 89 } 90 } 91 } catch (Exception e) { 92 e.printStackTrace(); 93 } finally { 94 lock.unlock(); 95 } 96 } 97 }
運行結果以下:
1 線程1 >>> 0 2 線程2 >>> 1 3 線程2 >>> 1 4 線程3 >>> 2 5 線程3 >>> 2 6 線程3 >>> 2 7 線程1 >>> 3 8 線程2 >>> 4 9 線程2 >>> 4 10 線程3 >>> 5 11 線程3 >>> 5 12 線程3 >>> 5 13 線程1 >>> 6 14 線程2 >>> 7 15 線程2 >>> 7 16 線程3 >>> 8 17 線程3 >>> 8 18 線程3 >>> 8 19 線程1 >>> 9
Condition中的await()、signal()、signalAll()與Object中的wait()、notify()、notifyAll()區別
使用方式不一樣
Condition中的await()方法至關於Object的wait()方法,Condition中的signal()方法至關於Object的notify()方法,Condition中的signalAll()至關於Object的notifyAll()方法。
不一樣的是,Object中的這些方法是和同步鎖捆綁使用的,而Condition是須要與互斥鎖/共享鎖捆綁使用的。
功能更增強大
Condition它更強大的地方在於:可以更加精細的控制多線程的休眠與喚醒。對於同一個鎖,咱們能夠建立多個Condition,在不一樣的狀況下使用不一樣的Condition。
例如,假如多線程讀/寫同一個緩衝區:當向緩衝區中寫入數據以後,喚醒「讀線程」。當從緩衝區讀出數據以後,喚醒「寫線程」。當緩衝區滿的時候,「寫線程」須要等待。當緩衝區爲空時,「讀線程」須要等待。
若是採用Object類中的wait()、notify()、notifyAll()實現該緩衝區,當向緩衝區寫入數據以後須要喚醒「讀線程」時,不可能經過notify()或notifyAll()明確的指定喚醒"讀線程",而只能經過notifyAll喚醒全部線程,可是notifyAll沒法區分喚醒的線程是讀線程,仍是寫線程。
可是,經過Condition,就能明確的指定喚醒讀線程。