應用場景:生產者和消費者問題
假設倉庫中只能存放一件產品,生產者將生產出來的產品放入倉庫,消費者將倉庫中的產品取走消費
如果倉庫中沒有產品,則生產者將產品放入倉庫,否則停止生產並等待,知道倉庫中的產品被消費者取走為止
如果倉庫中放有產品,則消費者可以將產品取走消費,否則停止消費並等待,知道倉庫中再次放入產品為止
分析
這是一個線程同步問題,生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件
對於生產者,沒有生產產品之前,要通知消費者等待,而生產了產品之后,又需要馬上通知消費者消費
對於消費者,在消費之后,要通知生產者已經結束消費,需要生產新的產品以供消費
在生產者消費者問題中,僅有synchronized是不夠的
synchronized可組織並發更新同一個共享資源,實現了同步
synchronized不能用來實現不同線程之間的消息傳遞(通信)
java提供了幾個方法解決線程之間的通信問題:
wait() 表示線程一直等待,直到其他線程通知,與sleep不同,會釋放鎖
wait(long timeout) 指定等待的毫秒數
notify() 喚醒一個處於等待狀態的線程
notifyAll() 喚醒同一個對象上所有調用wait()方法的線程,優先級別高的線程優先調度
注意:均是Object類的方法,都只能在同步方法或者同步代碼塊中使用,否則會拋出異常
解決方式1:管程法
生產者:負責生產數據的模塊
消費者:負責處理數據的模塊
緩沖區:消費者不能直接使用生產者的數據,他們之間有個緩沖區
生產者將生產好的數據放入緩沖區,消費者從緩沖區拿出數據
解決方式2:信號燈法
一個標志位,如果為真就等待,如果為假,就通知另一個
管程法
package com.gaoji;
//測試生產者消費者模型-->利用緩沖區解決:管程法
//生產者,消費者,產品,緩沖區
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生產者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生產了"+i+"只雞");
}
}
}
//消費者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消費了"+container.pop().id+"只雞");
}
}
}
//產品
class Chicken{
int id; //產品編號
public Chicken(int id) {
this.id = id;
}
}
//緩沖區
class SynContainer{
//需要一個容器大小
Chicken[] chickens = new Chicken[10];
//容器計數器
int count = 0;
//生產者放入產品
public synchronized void push(Chicken chicken){
//如果容器滿了,就需要等待消費者
if (count==chickens.length){
//通知消費者消費,生產等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
//可以通知消費者消費了
this.notifyAll();
}
//消費者消費產品
public synchronized Chicken pop(){
//判斷能否消費
if(count==0){
//等待生產者生產,消費者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消費
count--;
Chicken chicken = chickens[count];
//吃完了,通知生產者生產
this.notifyAll();
return chicken;
}
}
生產了0只雞
生產了1只雞
生產了2只雞
生產了3只雞
生產了4只雞
生產了5只雞
生產了6只雞
生產了7只雞
生產了8只雞
生產了9只雞
消費了9只雞
生產了10只雞
生產了11只雞
消費了10只雞
生產了12只雞
消費了11只雞
消費了12只雞
生產了13只雞
消費了13只雞
消費了14只雞
消費了8只雞
消費了7只雞
消費了6只雞
消費了5只雞
消費了4只雞
消費了3只雞
消費了2只雞
消費了1只雞
消費了0只雞
生產了14只雞
生產了15只雞
生產了16只雞
生產了17只雞
生產了18只雞
生產了19只雞
生產了20只雞
生產了21只雞
生產了22只雞
生產了23只雞
生產了24只雞
生產了25只雞
消費了19只雞
消費了25只雞
消費了26只雞
消費了24只雞
消費了23只雞
消費了22只雞
消費了21只雞
消費了20只雞
消費了18只雞
消費了17只雞
消費了16只雞
消費了15只雞
生產了26只雞
生產了27只雞
生產了28只雞
生產了29只雞
生產了30只雞
生產了31只雞
生產了32只雞
生產了33只雞
生產了34只雞
生產了35只雞
生產了36只雞
生產了37只雞
消費了27只雞
消費了37只雞
消費了36只雞
消費了35只雞
消費了34只雞
消費了33只雞
消費了32只雞
消費了31只雞
消費了30只雞
消費了29只雞
消費了28只雞
生產了38只雞
生產了39只雞
生產了40只雞
生產了41只雞
生產了42只雞
生產了43只雞
生產了44只雞
生產了45只雞
生產了46只雞
生產了47只雞
消費了47只雞
消費了48只雞
消費了46只雞
消費了45只雞
消費了44只雞
消費了43只雞
消費了42只雞
消費了41只雞
消費了40只雞
消費了39只雞
生產了48只雞
消費了38只雞
生產了49只雞
消費了49只雞
生產了50只雞
消費了50只雞
生產了51只雞
消費了51只雞
生產了52只雞
消費了52只雞
生產了53只雞
消費了53只雞
生產了54只雞
消費了54只雞
生產了55只雞
消費了55只雞
生產了56只雞
消費了56只雞
生產了57只雞
消費了57只雞
生產了58只雞
消費了58只雞
生產了59只雞
消費了59只雞
生產了60只雞
消費了60只雞
生產了61只雞