場景描述:
一個倉庫,生產者在工廠裡生產了產品後,將產品存放到倉庫裡,倉庫存放數量有限,當滿倉後,停 止生產,直到有消費著將產品消費後才繼續生產;消費者從倉庫裡提取產品,當倉庫空倉時,停止消費產 品,直到倉庫中有產品時,才繼續消費產品。
代碼的實現(調整線程sleep時間可以實現生產速度與消費速度的不同):
TestProduceAndConsumer.java
package com.nantian;
import java.util.Random;
public class TestProduceAndConsumer {
public static void main(String[] args) {
// 創建一個工廠對象
ProductFactory pf = new ProductFactory();
// 創建一個生產者和一個消費者,傳遞工廠的引用,保證兩者操作的是同一個工廠
Producer p = new Producer(pf);
Consumer c = new Consumer(pf);
// 啟動兩個線程
p.start();
c.start();
}
}
// 產品工廠
class ProductFactory {
// product表示倉庫
private char[] product = { ' ', ' ', ' ', ' ', ' '};
// flag標記產品數量
private int flag = 0;
// 生產產品
public synchronized void produceProduct(char p) throws InterruptedException {
// 判斷產品是否滿倉,以便決定是否繼續生產
if (flag == product.length) {
this.wait();
}
// 當代碼執行到這裡,一定不是滿倉狀態
product[flag++] = p;
// 查看此時倉庫狀態(這裡不屬於業務邏輯部分)
System.out.print(p + "被生產,當前倉庫狀態:");
for (char tmp : product) {
System.out.print(tmp);
}
System.out.println();
// 生產方法完成,如果存在等待隊列中的線程,應該喚醒
this.notifyAll();
}
// 消費產品
public synchronized char consumeProduct() throws InterruptedException {
// 判斷倉庫是否空倉,以便決定是否消費產品
if(flag == 0) {
this.wait();
}
// 當代碼執行到這裡,一定不是空倉狀態
char p = product[--flag]; product[flag]=' ';
// 查看此時倉庫狀態(這裡不屬於業務邏輯部分)
System.out.print(p + "被消費,當前倉庫狀態:");
for(char tmp : product) {
System.out.print(tmp);
}
System.out.println();
// 消費方法完成,如果存在等待隊列中的線程,應該喚醒
this.notifyAll();
return p;
}
}
// 生產者
class Producer extends Thread {
private ProductFactory pf = null;
public Producer(ProductFactory pf) {
this.pf = pf;
}
public void run() {
// 一共生產20個產品
for(int i=0; i<20; i++) {
// 隨機產生一個大寫字母作為產品
Random r = new Random();
char p = (char)(r.nextInt(26) + 'A');
try {
// 產品入庫
pf.produceProduct(p);
// 故意sleep,以便消費線程有機會獲得CPU時間片,方便演示
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消費者
class Consumer extends Thread {
private ProductFactory pf = null;
public Consumer(ProductFactory pf) {
this.pf = pf;
}
public void run() {
// 一共消費20個產品
for(int i=0; i<20; i++) {
try {
// 產品出庫
pf.consumeProduct();
// 故意sleep,以便生產線程有機會獲得CPU時間片,方便演示
// sleep時間稍微錯開,阻止同時競爭CPU時間片
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}