Java多線程之線程間的通信

1. 線程間的通信

  • 線程間通信

    • 生產者+消費者
    • 通知等待喚醒機制
  • 多線程編程模板

    • 判斷 干活 通知
    • 判斷需使用while,以防止中斷和虛假喚醒(見java.lang.Object的API)

    A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one

       synchronized (obj) {
            while (<condition does not hold>)
                obj.wait(timeout);
            ... // Perform action appropriate to condition
       }
    

    線程也可以在沒有通知、中斷或超時的情況下被喚醒,這就是所謂的假喚醒。雖然這種情況在實踐中很少發(fā)生,但應用程序必須通過測試導致線程被喚醒的條件來防止這種情況發(fā)生,如果條件不滿足,則繼續(xù)等待。換句話說,等待應該總是出現(xiàn)在循環(huán)中,就像這個循環(huán)一樣

1.1 synchronized版

  • synchronized實現(xiàn)

    • 先2個線程操作資源類,資源中的操作判斷使用if,如線程A和線程B,可以正常運行1 0 1 0 1 0...
    • 增加2個線程C和D,模擬虛假喚醒,判斷依舊是if,運行的結果數(shù)字不是全部為1、0
      • 原因:在java多線程判斷時,不能用if,程序出事出在了判斷上面,突然有一添加的線程進到if了,突然中斷了交出控制權,沒有進行驗證,而是直接走下去了,加了兩次,甚至多次
    • 在4線程中,將資源類的if判斷改為while判斷,while是只要喚醒就要拉回來再判斷一次
    package juc.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Description:
     *  現(xiàn)在兩個線程,
     *  可以操作初始值為零的一個變量,
     *  實現(xiàn)一個線程對該變量加1,一個線程對該變量減1,
     *  交替,來10輪。
     * @Package: juc.demo
     * @ClassName NotifyWaitDemo
     * @author: wuwb
     * @date: 2020/10/19 13:30
     */
    public class NotifyWaitDemo {
        public static void main(String[] args) {
            int turn = 1000;
            //資源類
            ShareData data = new ShareData();
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "A").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "B").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "C").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "D").start();
        }
    }
    
    //資源類
    class ShareData{
        private int number = 0;
    
        public synchronized void increment() throws InterruptedException {
            //判斷  if換while
            while (number != 0) {
                this.wait();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            //通知
            this.notifyAll();
        }
    
        public synchronized void decrement() throws InterruptedException {
            while (number == 0) {
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            this.notifyAll();
        }
    
    }
    
    

1.2 JUC版

1608004387960.png
  • Lock 及 Condition

    package juc.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Description:
     *  現(xiàn)在兩個線程,
     *  可以操作初始值為零的一個變量,
     *  實現(xiàn)一個線程對該變量加1,一個線程對該變量減1,
     *  交替,來10輪。
     * @Package: juc.demo
     * @ClassName NotifyWaitDemo
     * @author: wuwb
     * @date: 2020/10/19 13:30
     */
    public class NotifyWaitDemo {
        public static void main(String[] args) {
            int turn = 1000;
            //資源類
            ShareData data = new ShareData();
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "A").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "B").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "C").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "D").start();
        }
    }
    
    //資源類
    class ShareData{
        private int number = 0;
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void increment() {
            lock.lock();
            try {
                //判斷
                while (number != 0) {
                    condition.await();//this.wait();
                }
                //干活
                number++;
                System.out.println(Thread.currentThread().getName() + ":" + number);
                //通知
                condition.signalAll();//this.notifyAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void decrement() {
            lock.lock();
            try {
                while (number == 0) {
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName() + ":" + number);
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
    }
    
    

1.3 定制化調用通信

  • 使用Lock、Condition指定調用順序,指定喚醒哪個線程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description
 * 多線程之間按順序調用,實現(xiàn)A->B->C
 * ......來10輪
 */
public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResource resource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printC();
            }
        }, "C").start();
    }
}

class ShareResource{
    /**標志位*/
    private int number = 1;
    private Lock lock = new ReentrantLock();
    /**3把鑰匙*/
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
   
    public void printA() {
        lock.lock();
        try {
            while (number != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>AAAAAAAAAA");
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>BBBBBBBBBB");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>CCCCCCCCCC");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 一,介紹 本總結我對于JAVA多線程中線程之間的通信方式的理解,主要以代碼結合文字的方式來討論線程間的通信,故摘抄...
    那些年的代碼閱讀 211評論 0 0
  • 多線程之間的通信 線程通信概念:線程是操作系統(tǒng)中獨立的個體,但這些個體如果不經過特殊處理就不能成為一個整體,線程間...
    CQ_TYL閱讀 1,007評論 0 0
  • 1.多線程之等待與通知機制1.1什么是等待通知機制?在生活中,如我們去飯店,服務員拿菜單給我們點菜,然后記錄完告訴...
    學編程的小屁孩閱讀 184評論 0 1
  • 【 線程間的通訊 wait()在對象上等待,等待通知(在等待過程中釋放對象鎖、等待必須在同步塊內、這個對象就是同步...
    征程_Journey閱讀 786評論 0 0
  • 漸變的面目拼圖要我怎么拼? 我是疲乏了還是投降了? 不是不允許自己墜落, 我沒有滴水不進的保護膜。 就是害怕變得面...
    悶熱當乘涼閱讀 4,480評論 0 13

友情鏈接更多精彩內容