Thread 線程協作-Object對象的wait、notify和notifyAll方法原理,通過notifyAll和wait方法完成生產者和消費者模型案例

  • 線程協作

所謂線程協作也就是多線程併發編程中,線程與線程之間完成同一個業務邏輯,期間可能有線程等待、線程通知。

  • 線程等待實現方式

Thread線程類的sleep方法,等待指定時間後自動喚醒線程。Object類的wait 方法,線程睡眠後只能通過Object的notify或notifyAll喚醒線程。

notify 方法隨機喚醒多個等待線程中的一個,而notifyAll方法則喚醒所有等待線程。

  • wait、notify或notifyAll方法使用注意項
  1. wait和notify使用的模範代碼邏輯如下
    等待通知的代碼邏輯一般爲:
    synchronized(對象){
    	while(條件不滿足){
    		對象.wait();
    	}
    	//業務邏輯
    }
    
    接收通知的代碼邏輯一般爲:
    synchronized(對象){
    	//業務邏輯,改變條件
    	對象.notify/notifyAll();
    }

     

  2. wait和notify方法使用必須添加synchronized鎖,否則報異常 java.lang.IllegalMonitorStateException

  3. 鎖對象和wait、notify調用的對象必須是同一個對象

  4. 一個方法中可以同時出現wait和notify或notifyAll,沒有任何影響,只是取決於業務邏輯

注意:wait和notify方法使用都要加鎖,線程調用wait方法後,另一個線程怎麼還會獲取到鎖

wait方法內部原理,wait方法調用時其實是把當前線程在wait代碼行處進行休眠,同時釋放鎖

  • wait和notifyAll方法案例,實現消費者和生產者模式

一個線程創建橘子到隊列中,隊列滿了通知監視官,同時休眠。另一個線程判斷隊列中有橘子則進行消費,沒有則通知監視官,同時休眠。監視官接到通知後,分別派遣創建線程或消費線程進行創建或消費,從而讓系統保持平衡進行。

創建和消費代碼如下:


import java.util.Queue;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 生產橘子,消費橘子
 */
public class CreConsumerOrange {
    //工廠監視員(notify/wait 鎖對象).不管是生產者,還是消費者,做完本職工作都要通知一下監視官
    private Object monitorLock;
    //創建橘子隨機大小
    private static final Random random=new Random();
    private static final int queueSize=5;
    private static final Queue<Integer> queue=new LinkedBlockingQueue<>();
    public CreConsumerOrange(Object monitor){
        this.monitorLock=monitor;
    }
    //生產線程-生產,創建橘子,隊列小於5則創建
    public void createOrange(){
        while(true) {
            synchronized (monitorLock){
                //生產
                if(queue.size()<queueSize){
                    while (queue.size()<queueSize) {
                        int orange = random.nextInt(20);
                        queue.add(orange);
                        System.out.println(Thread.currentThread().getName()+"===========create orange "+orange);

                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //通知monitorLock ,橘子已經創建完成
                    monitorLock.notifyAll();
                }
                else{
                    try {
                        monitorLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    //消費線程-消費
    public void consumeOrange(){
        while(true) {
            synchronized (monitorLock) {
                if(queue.size()>0) {
                    while (queue.size() >= queueSize) {
                        Integer peek = queue.remove();
                        System.out.println(Thread.currentThread().getName()+"=======consumeOrage size is " + peek);
                    }
                    monitorLock.notifyAll();
                }else{
                    try {
                        monitorLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            //模擬消費者話費時間
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

創建線程進行消費代碼如下:1個線程創建,同時2個線程進行消費

public class NotifyTest {
    public static void main(String[] args) {
        Object monitor =new Object();//監視官
        CreConsumerOrange factory=new CreConsumerOrange(monitor);

        //消費者
        new Thread(new Runnable() {
            @Override
            public void run() {
                factory.consumeOrange();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                factory.consumeOrange();
            }
        }).start();

        //生產者
        new Thread(new Runnable() {
            @Override
            public void run() {
                factory.createOrange();
            }
        }).start();

        System.out.println("===============main is end===================");
    }
}

執行結果如下:

===============main is end===================
Thread-2===========create orange 7
Thread-2===========create orange 0
Thread-2===========create orange 11
Thread-2===========create orange 3
Thread-2===========create orange 12
Thread-1=======consumeOrage size is 7
Thread-2===========create orange 14
Thread-0=======consumeOrage size is 0
Thread-2===========create orange 0
Thread-1=======consumeOrage size is 11
Thread-2===========create orange 16
Thread-0=======consumeOrage size is 3
Thread-2===========create orange 19
...
...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章