Java併發包——線程通訊

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,就能明確的指定喚醒讀線程。

相關文章
相關標籤/搜索