線程間通信(也叫線程並發協作)的四種方式


參考博客:https://crossoverjie.top/2018/03/16/java-senior/thread-communication/

線程間通信一共有以下四種方式:

1、wait()、notify() 、notifyAll() - 等待通知機制,這些方法屬於java.lang.Object類的方法,只能在同步方法或者代碼塊中使用

2、Volatile 共享內存

3、CountDownLatch  並發工具

4、CyclicBarrier 並發工具

 


wait/notify

兩個線程通過對同一對象調用等待 wait() 和通知 notify() 方法來進行通訊。

例如:兩個線程交替打印奇偶數,通過wait/notify實現

copypublic class WaitNotify { // 狀態鎖 private static Object lock = new Object(); private static Integer i = 0; public void odd() { while (i < 10) { synchronized (lock) { if (i % 2 == 1) { System.out.println(Thread.currentThread().getName() + " - " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i++; lock.notify(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public void even() { while (i < 10) { synchronized (lock) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + " - " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i++; lock.notify(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public static void main(String[] args) { WaitNotify waitNotify = new WaitNotify(); Thread t1 = new Thread(() -> waitNotify.odd(), "線程1"); Thread t2 = new Thread(() -> waitNotify.even(), "線程2"); t1.start(); t2.start(); } }

結果:

 
          
copy線程2 - 0 線程1 - 1 線程2 - 2 線程1 - 3 線程2 - 4 線程1 - 5 線程2 - 6 線程1 - 7 線程2 - 8 線程1 - 9 線程2 - 10

Volatile 共享內存 

Volatile 修飾內存可見性。因為 Java 是采用共享內存的方式進行線程通信的,所以可以采用以下方式用主線程關閉 A 線程:

Volatile詳情請看這里:https://crossoverjie.top/2018/03/09/volatile/

copypublic class Volatile implements Runnable {

  // flag采用volatile修飾,主要是為了內存可見性
private static volatile Boolean flag = true; @Override public void run() { while (flag) { System.out.println(Thread.currentThread().getName() + " - 執行"); } System.out.println("線程結束"); } public static void main(String[] args) { Thread t = new Thread(new Volatile()); t.start(); try { Thread.sleep(5); flag = false; } catch (InterruptedException e) { e.printStackTrace(); } } } 結果: copyThread-0 - 執行 Thread-0 - 執行 Thread-0 - 執行 Thread-0 - 執行 Thread-0 - 執行 線程結束

CountDownLatch

CountDownLatch可以代替wait/notify的使用, 並去掉synchronized。 CountDownLatch 可以實現 join 相同的功能,但是更加的靈活。

CountDownLatch 也是基於 AQS(AbstractQueuedSynchronizer) 實現的

下面重寫第一個例子:

copyimport java.util.concurrent.CountDownLatch;

public class CountDown {
  private static Integer i = 0;
  final static CountDownLatch countDown = new CountDownLatch(1);

  public void odd() {
    while (i < 10) {
      if (i % 2 == 1) {
        System.out.println(Thread.currentThread().getName() + " - " + i);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        i++;
        countDown.countDown();
      } else {
        try {
          countDown.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public void even() {
    while (i < 10) {
      if (i % 2 == 0) {
        System.out.println(Thread.currentThread().getName() + " - " + i);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        i++;
        countDown.countDown();
      } else {
        try {
          countDown.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) {

    CountDown countDown = new CountDown();

    Thread t1 = new Thread(() -> countDown.odd(), "線程1");
    Thread t2 = new Thread(() -> countDown.even(), "線程2");

    t1.start();
    t2.start();
  }
}

CyclicBarrier 並發工具

CyclicBarrier 中文名叫做屏障或者是柵欄,也可以用於線程間通信。

它可以等待 N 個線程都達到某個狀態后繼續運行的效果。

  1. 首先初始化線程參與者。
  2. 調用 await() 將會在所有參與者線程都調用之前等待。
  3. 直到所有參與者都調用了 await() 后,所有線程從 await() 返回繼續后續邏輯。
該工具可以實現 CountDownLatch 同樣的功能,但是要更加靈活。甚至可以調用 reset() 方法重置 CyclicBarrier (需要自行捕獲 BrokenBarrierException 處理) 然后重新執行。

copyimport java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(3); new Thread(() -> { System.out.println(Thread.currentThread().getName() + ": 准備..."); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("全部啟動完畢!"); }, "線程1").start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + ": 准備..."); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("全部啟動完畢!"); }, "線程2").start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + ": 准備..."); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("全部啟動完畢!"); }, "線程3").start(); } } 結果: copy線程3: 准備... 線程2: 准備... 線程1: 准備... 全部啟動完畢! 全部啟動完畢! 全部啟動完畢!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM