淺談Java簡單實現的生產者與消費者問題
阿新 • • 發佈:2018-11-13
一、面對生產者和消費者的問題,首先我們得明白幾點:
生產者:生產資料;
消費者:消費資料。
消費者在沒有資料可供消費的情況下,不能消費;
生產者在原資料沒有被消費掉的情況下,不能生產新資料。
假設,資料空間只有一個。
實際上,如果實現了正確的生產和消費,則,兩個執行緒應該是嚴格的交替執行。
synchronized關鍵字若用在程式碼中,形成一個同步塊,且,必須要執行鎖:
synchronized (鎖物件) {
同步塊
}
同步塊使得鎖物件稱為thread monitor
二、程式碼實現:
1.首先我們建立一個生產者和消費者共同擁有的鎖的類:
1 package com.mec.about_procuder_customer.core; 2 3 public class ProcuderCustomer { 4 //初始狀態的資料為0個 5 protected static volatile int count = 0; 6 //執行鎖 7 protected final static Object lock = new Object(); 8 }
上述程式碼有一個關鍵字volatile,它是保證執行緒之間有序的一種方式,最重要也是最直接的是禁止暫存器優化 。就是如果執行緒run方法中只是一個迴圈,並沒有執行語句,那麼,這個執行緒將不會執行。
2.我們再來建立一個生產者的類:
1 package com.mec.about_procuder_customer.core; 2 3 //生產者 4 public class Procuder extends ProcuderCustomer implements Runnable { 5 6 //存放資料的空間 7 private int[] dataSpace; 8 9 public Procuder(int[] dataSpace, String threadName) {10 this.dataSpace = dataSpace; 11 //啟動執行緒 12 new Thread(this, threadName).start(); 13 } 14 15 @Override 16 public void run() { 17 int i = 0; 18 19 while (true) { 20 synchronized (lock) { 21 //判斷是否空間已滿 22 if (count < dataSpace.length) { 23 //產生者放資料 24 dataSpace[count] = i++; 25 System.out.println("[" + Thread.currentThread().getName() 26 + "]執行緒生產了一個數:" + dataSpace[count++] 27 + " " + count); 28 try { 29 //只是為了看的清楚,沉睡2秒 30 Thread.sleep(200); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 //喚醒消費者 35 lock.notify(); 36 } else { 37 try { 38 //使自己處於阻塞狀態 39 lock.wait(); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 } 44 } 45 } 46 } 47 }
3.建立消費者的類:
1 package com.mec.about_procuder_customer.core; 2 3 //消費者 4 public class Customer extends ProcuderCustomer implements Runnable { 5 //存放資料的空間 6 private int[] dataSpace; 7 8 public Customer(int[] dataSpace, String threadName) { 9 this.dataSpace = dataSpace; 10 //啟動執行緒 11 new Thread(this, threadName).start(); 12 } 13 14 @Override 15 public void run() { 16 while (true) { 17 //加鎖 18 synchronized (lock) { 19 //判斷是否有資料 20 if (count > 0) { 21 System.out.println("[" + Thread.currentThread().getName() 22 + "]執行緒消費了一個數:" + dataSpace[--count]); 23 //喚醒生產者 24 lock.notifyAll(); 25 } else { 26 try { 27 //使自己處於阻塞狀態 28 lock.wait(); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 } 34 } 35 } 36 37 }
4.測試類:
1 package com.mec.about_procuder_customer.test; 2 3 import com.mec.about_procuder_customer.core.Customer; 4 import com.mec.about_procuder_customer.core.Procuder; 5 6 public class Test { 7 8 public static void main(String[] args) { 9 int[] data = new int[10]; 10 new Procuder(data, "生產者1"); 11 new Procuder(data, "生產者2"); 12 new Customer(data, "消費者"); 13 } 14 15 }
執行結果: