java 多線程學習筆記------(4)線程協作(併發控制)
線程協作:
生產者消費者模式
應用場景:生產者和消費者問題
• 假設倉庫中只能存放一件產品,生產者將生產出來的產品放入 倉庫,消費者將倉庫中產品取走消費;
• 如果倉庫中沒有產品,則生產者將產品放入倉庫,否則停止生 產並等待,直到倉庫中的產品被消費者取走爲止;
• 如果倉庫中放有產品,則消費者可以將產品取走消費,否則停 止消費並等待,直到倉庫中再次放入產品爲止。
分析:這是一個線程同步問題,生產者和消費者共享同一個資源,並且生產者和消 費者之間相互依賴,互爲條件
• 對於生產者,沒有生產產品之前,要通知消費者等待。而生產了產品之後, 又需要馬上通知消費者消費
• 對於消費者,在消費之後,要通知生產者已經消費結束,需要繼續生產新 產品以供消費
• 在生產者消費者問題中,僅有synchronized是不夠的
• synchronized可阻止併發更新同一個共享資源,實現了同步
• synchronized不能用來實現不同線程之間的消息傳遞(通信)
解決方式1:併發協作模型“生產者/消費者模式” 管程法
• 生產者:負責生產數據的模塊(這裏模塊可能是:方法、對象、線程、進程);
• 消費者:負責處理數據的模塊(這裏模塊可能是:方法、對象、線程、進程);
• 緩衝區:消費者不能直接使用生產者的數據,它們之間有個“緩衝區”; 生產者將生產好的數據放入“緩衝區”,消費者從“緩衝區”拿要處理的數據。
解決方式2:併發協作模型“生產者/消費者模式” 信號燈法
Java提供了3個方法解決線程之間的通信問題
均是java.lang.Object類的方法 都只能在同步方法或者同步代碼塊中使用,否則會拋出異常
管程法:
package Thread;
/**
* 協作模型:生產者消費者實現方式一:
* 管程法:藉助緩衝區
* @author 賭徒
*
*/
public class Cooperation {
public static void main(String[] args) {
Syncontiner continer=new Syncontiner();
new Productor(continer).start();
new Consumer(continer).start();
}
}
//生產者
class Productor extends Thread{
Syncontiner continer;
public Productor(Syncontiner continer) {
this.continer = continer;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生產--->第"+i+"個");
continer.push(new Datas(i));
}
}
}
//消費者
class Consumer extends Thread{
Syncontiner continer;
public Consumer(Syncontiner continer) {
this.continer = continer;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消費--->第"+continer.pop().ID+"個");
}
}
}
//緩衝區
class Syncontiner{
Datas[]datas=new Datas[10];//存儲數據容器
int count=0;//計數器
//儲存 生產
public synchronized void push(Datas data) {
//何時生產 容器有空間
//不能生產 等待
if (count==datas.length) {
try {
this.wait();//線程阻塞 消費者通知生產,阻塞取消
} catch (InterruptedException e) {
e.printStackTrace();
}
}
datas[count]=data;
count++;
this.notifyAll();//存在數據 喚醒
}
//獲取 消費
public synchronized Datas pop() {
//何時消費 容器中是否有數據
//沒有數據等待
if (count==0) {
try {
this.wait();//線程阻塞 生產者通知消費,阻塞取消
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Datas data=datas[count];
this.notifyAll();//存在空間 喚醒
return data;
}
}
//數據
class Datas{
int ID;
public Datas(int iD) {
ID = iD;
}
}
信號燈法:
package Thread;
/**
* 協作模型:生產者消費者實現方式二:
*信號燈法
* @author 賭徒
*
*/
public class Cooperation1 {
public static void main(String[] args) {
TV tv=new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生產者 演員
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i <20; i++) {
if (i%2==0) {
this.tv.play("你好");
}else {
this.tv.play("嗨");
}
}
}
}
//消費者 觀衆
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//同一個資源 電視
class TV{
String voice;
//信號燈
//T 表示演員表演 觀衆等待
//F 表示觀衆觀看 演員等待
boolean flag=true;
//表演
public synchronized void play(String voice) {
//演員等待
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了:"+voice);
this.voice=voice;
this.notifyAll();
this.flag=!this.flag;
}
//觀看
public synchronized void watch() {
//觀衆等待
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.err.println("聽到了:"+voice);
this.notifyAll();
this.flag=!this.flag;
}
}