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();
}
}
執行結果完全是生產一個,消費一個,不會出現生產一次消費兩次或者生產完不消費的情況。
你以為的極限,也許只是別人的起點