1. 程式人生 > 其它 >JAVA多執行緒之生產消費模型

JAVA多執行緒之生產消費模型

生產消費模型

所謂生產消費模型,是通過一個容器來解決生產者和消費者的強耦合問題。通俗的講,就是生產者不斷的生產,
消費之也在不斷消費,消費者消費的產品是生產者生產的,這就必然存在一箇中間的容器,我們可以把這個容器
想象成一個倉庫,當倉庫沒有滿的時候,生產者生產產品,當倉庫滿了的時候,生產者不能繼續生產產品,而需
處於等待狀態。當倉庫不為空的時候,消費者可以消費產品,當倉庫為空的時候,消費者不能再消費產品,而應
處於等待狀態。這樣不斷迴圈,在這個過程中,生產者和消費者是不直接傑出的,所謂的倉庫其實就是一個阻塞
佇列,生產者生產的產品不直接提供給消費者,而是提供給阻塞佇列,這個阻塞佇列就是來解決生產者和消費者
之間的強耦合,這就是生產者消費者模型。

wait()方法

1、注意wait()是Object裡面的方法,而不是Thread裡面的。它的作用是將當前執行緒置於預執行佇列,並在wait()
所在的程式碼處停止,等待喚醒通知。
2、wait()只能在同步程式碼塊或同步方法中執行,如果呼叫wait()方法,而沒有持有適當的鎖,就會丟擲異常。
wait()方法呼叫後會釋放出鎖,執行緒與其他執行緒競爭重新獲取鎖。
3、執行緒呼叫了wait()方法後一直在等待,不會繼續往下執行,wait()一旦執行,除非接受到喚醒操作或者異常
中斷,否則不會往下執行

notify()方法

1、notify()方法也是要在同步程式碼塊或者同步方法中呼叫的,它的作用是使停止的執行緒繼續執行,呼叫notify()
方法後,會通知那些等待當前執行緒物件鎖的執行緒,並使它們重新獲取該執行緒的物件鎖,如果等待執行緒比較多的時候,
則由執行緒規劃器隨機挑選出一個呈現wait狀態的執行緒。
2、notify()呼叫之後不會立即釋放鎖,而是當執行notify()的執行緒執行完成,即退出同步程式碼塊或同步方法時,
才會釋放物件鎖。

notifyAll()

喚醒所有處於等待狀態的執行緒,一般使用notifyAll()比較多,因為notify隨機喚醒一個執行緒,可能不是我們想要的
造成程式出現問題,notifyAll()喚醒所有等待執行緒則一定會得到我們想要的

例程

測試1,不加入同步和執行緒通訊

Test類

package com.lding.test2;

public class Test {
    public static void main(String[] args) {
        Queue queue=new Queue();
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }
}

Queue類

package com.lding.test2;

public class Queue {
    private int n;

    public int getN() {
        System.out.println("消費:"+n);
        return n;
    }

    public void setN(int n) {
        System.out.println("生產:"+n);
        this.n = n;
    }
}

Producer類

package com.lding.test2;

public class Producer implements Runnable{
    Queue queue;
    Producer(Queue queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        int i=0;
        while (true){
            queue.setN(i++);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Consumer類

package com.lding.test2;

public class Consumer implements Runnable{
    Queue queue;
    Consumer(Queue queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while (true){
            queue.getN();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

執行結果

可以看到,0生產了一次卻消費了兩次。

測試2 加上同步

Queue類

package com.lding.test2;

public class Queue {
    private int n;

    public synchronized int getN() {
        System.out.println("消費:"+n);
        return n;
    }

    public synchronized void setN(int n) {
        System.out.println("生產:"+n);
        this.n = n;
    }
}


生產了18並沒有消費
生產了22消費了兩次22

版本3 加上執行緒之間的通訊

Queue類

package com.lding.test2;

public class Queue {
    private int n;
    boolean flag=false;//flag=false 容器中無資料, flag=true 容器中有商品
    public synchronized int getN() {
        if(!flag){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消費:"+n);
        flag=false;//消費完畢 容器沒資料
        notifyAll();
        return n;
    }

    public synchronized void setN(int n) {
        if(flag){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生產:"+n);
        this.n = n;
        flag=true;//生產完畢 容器中有資料
        notifyAll();
    }
}

執行結果完全是生產一個,消費一個,不會出現生產一次消費兩次或者生產完不消費的情況。

你以為的極限,也許只是別人的起點