天天看點

線程間通訊

wait、notify、notifyAll

  • 簡介
1、在多線程環境下,有時候一個線程的執行,依賴于另外一個線程的某種狀态的改變,這個時候,我們就可以使用wait與notify或者notifyAll
2、wait跟sleep的差別:wait會釋放持有的鎖,而sleep不會,sleep隻是讓線程在指定的時間内,不去搶占cpu的資源 
3、注意點:wait notify必須放在同步代碼塊中, 且必須擁有目前對象的鎖,即不能取得A對象的鎖,而調用B對象的wait 哪個對象wait,就得調哪個對象的notify
4、notify跟notifyAll的差別:nofity随機喚醒一個等待的線程 notifyAll喚醒所有在該對象上等待的線程      
  • 案例1
public class Demo {

    private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        // 線程1,while循環中的參數一直為false,需要等線程2先執行完
        new Thread(()->{
            while (!flag) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("flag is false");
            }
            System.out.println("flag is true");
        }).start();

        // 休眠
        Thread.sleep(1000L);

        // 線程2,将flag設定為true,線程1需要等待線程2執行完
        new Thread(()->{
            flag = true;
        }).start();
    }

}

# 控制台結果:
flag is false
flag is true      
  • 案例2
public class Demo1 {

    private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {

        Object obj = new Object();

        new Thread(()->{
            while (!flag) {    // 為true
                synchronized (obj) {        // 擷取鎖
                    try {
                        System.out.println("flag is false");     // (1)
                        obj.wait();     // 挂起線程
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("flag is true");     // (3)
        }).start();

        new Thread(()->{
            while (!flag) {
                synchronized (obj) {
                    try {
                        System.out.println("flag is false");     // (2)
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("flag is true");     // (4)
        }).start();

        // 休眠1秒
        Thread.sleep(1000L);

        new Thread(()->{
            flag = true;
            synchronized (obj) {
                obj.notifyAll();        // 喚醒所有等待的線程
            }
        }).start();
    }

}

# 控制台
flag is false
flag is false
flag is true
flag is true      

等待通知經典模型之生産者消費者

  • 代碼案例
# 中間商
public class Medium {

    private int num = 0;
    // 庫存量
    private static final int TOTAL = 20;

    /**
     * 接收生産資料
     */
    public synchronized void put() {
        //判斷目前的庫存,是否已經是最大的庫存容量
        if (num < TOTAL) {
            //如果不是,生産完成之後,通知消費者進行消費
            System.out.println("新增庫存-------->目前庫存" + ++num);
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            notifyAll();        // 喚醒所有等待線程
        } else {
            //如果是,則通知生産者進行等待
            try {
                System.out.println("新增庫存---------> 庫存已滿"+num);
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 擷取消費資料
     */
    public synchronized void take() {
        //判斷目前庫存是否不足
        if (num > 0) {
            //如果充足,在消費完成之後通知生産者進行生産
            System.out.println("消費庫存-----------> 目前庫存容量" + --num);
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            notifyAll();
        } else {
            //如果不足,通知消費者暫停消費
            System.out.println("消費庫存-----------> 庫存不足"+num);
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

# 生産者
public class Producer implements Runnable {

    private Medium medium;

    public Producer(Medium medium) {
        this.medium = medium;
    }

    @Override
    public void run() {
        while (true) {
            medium.put();
        }
    }

}

# 消費者
public class Consumer implements Runnable{

    private Medium medium;

    public Consumer(Medium medium) {
        this.medium = medium;
    }

    @Override
    public void run() {
        while (true) {
            medium.take();
        }
    }

}

# 測試類
public class Main {

    public static void main(String[] args) {
        Medium medium = new Medium();
        // new 3個消費者
        new Thread(new Consumer(medium)).start();
        new Thread(new Consumer(medium)).start();
        new Thread(new Consumer(medium)).start();
        // new 5個生産者
        new Thread(new Producer(medium)).start();
        new Thread(new Producer(medium)).start();
        new Thread(new Producer(medium)).start();
        new Thread(new Producer(medium)).start();
        new Thread(new Producer(medium)).start();
    }

}