Java多執行緒-04-執行緒協作
阿新 • • 發佈:2022-04-18
目錄
一.執行緒簡介
二.執行緒建立
三.執行緒狀態
四.執行緒同步
五.執行緒協作
五.執行緒協作
生產者消費者問題
為什麼需要執行緒通訊:為了解決生產者和消費者問題
問題分析
解決執行緒通訊的方法
解決方式1:管程法
解決方式2:訊號燈法
通過一個標誌位來判斷
管程法
例子:
生產者:廚師製作炸雞
消費者:吃炸雞
緩衝區:取餐檯(容器,用來放炸雞)
如果不使用管程法,就只能讓廚師先做完100只炸雞,然後顧客吃掉100只炸雞,這顯然不合理
- wait() 會讓當前持有物件鎖的執行緒等待並釋放鎖
A.wait() 會讓當前持有A物件鎖的執行緒等待並釋放鎖
-
notifyAll() 喚醒正在等待此物件鎖的所有執行緒
A.notifyAll() 喚醒正在等待A物件鎖的所有執行緒
我說:
1.誰來呼叫兩個方法?
被消費者和生產者操作的物件,即緩衝區
2.生產者何時等待何時被喚醒?
緩衝區滿時生產者自行等待
生產者消費後喚醒生產者
3.消費者何時等待何時被喚醒?
緩衝區空時消費者自行等待
生產者生產後喚醒消費者
package 四執行緒協作; //生產者和消費者問題:利用緩衝區解決(管程法) //例子 //生產者:廚師製作炸雞 //消費者:吃炸雞 //緩衝區:取餐檯(容器,用來放炸雞) public class MonitorMethod { public static void main(String[] args) { PC_Container container = new PC_Container(); new Productor("|廚師|", container).start(); new Consumer("||顧客||", container).start(); } } //生產者執行緒:廚師製作炸雞 class Productor extends Thread{ public PC_Container container; public Productor(String name, PC_Container container){ super(name); this.container = container; } @Override public void run() { //一共製作100只炸雞 for (int i = 1; i <= 100; i++) { //製作一隻炸雞並放到前臺 container.push(new Chicken(i)); System.out.println(this.getName() + "-->生產了第"+ i + "只炸雞"); } } } //消費者執行緒:吃炸雞 class Consumer extends Thread{ public PC_Container container; public Consumer(String name, PC_Container container){ super(name); this.container = container; } @Override public void run() { //一共吃100只炸雞 for (int i = 1; i <= 100; i++) { //從前臺取一隻炸雞 Chicken chicken = container.pop(); System.out.println(this.getName() + "-->消費了第"+ chicken.id + "只炸雞"); } } } //炸雞 class Chicken { //炸雞編號 public int id; public Chicken(int id){ this.id = id; } } //緩衝區:取餐檯(容器,用來放炸雞) //會被生產者和消費者修改(多個執行緒操作同一個物件) class PC_Container { //容器大小:前臺最多能放10只炸雞 public Chicken[] chickens = new Chicken[10]; //容器計數器 public int count = 0; //放入炸雞 //該方法涉及修改PC_Container,使用同步方法(監視器是this),即鎖PC_Container public synchronized void push(Chicken chicken){ //容器滿了-前臺已經放滿10只炸雞了 if(count == chickens.length){ //讓生產者等待 try { //wait()會讓當前持有PC_Container物件鎖的執行緒等待並釋放鎖 //持有鎖並且跑push方法的只能是生產者執行緒,所以這裡的this.wait()會讓生產者執行緒等待 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以生產 //這裡不需要寫else if,因為如果生產者處於等待,那麼下面的程式碼就不會執行了 //沒有滿,丟入炸雞 chickens[count] = chicken; count++; //現在前臺有炸雞了,需要喚醒消費者 //notifyAll()喚醒正在等待此物件監視器鎖的所有執行緒 //notifyAll()可以喚醒生產者和消費者,但此時生產者本來就是醒,所以旨在喚醒消費者來吃炸雞 this.notifyAll(); } //放入炸雞 //該方法涉及修改PC_Container,使用同步方法(監視器是this),即鎖PC_Container public synchronized Chicken pop(){ //容器空了-前臺沒有炸雞了 if(count == 0){ //讓消費者等待 try { //跑pop方法的只能是消費者執行緒,所以這裡的this.wait()會讓消費者執行緒等待 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消費 //取走炸雞 count--;//注意這裡要先減,count下標指向的是陣列中下一個空白的位置,count-1才是最新放入的炸雞 Chicken chicken = chickens[count]; //因為消費者取走了炸雞所以現在前臺一定不是滿的 //需要通知生產者生產 this.notifyAll(); return chicken; } }
訊號燈法
例子
生產者:演員表演節目
消費者:觀眾收看節目
演員和生產者都操作同一個電視機
演員通過 電視機上的訊號燈 判斷自己的狀態
1.等待
2.表演節目,之後提醒觀眾觀看
觀眾通過 電視機上的訊號燈 判斷自己的狀態
1.等待
2.收看節目,之後提醒演員表演
package 四執行緒協作.生產者消費者問題; import java.security.PublicKey; //生產者和消費者問題:利用標誌位解決(訊號燈法) //例子 //生產者:演員表演節目 //消費者:觀眾收看節目 public class SignalLight { public static void main(String[] args) { TV tv = new TV(); new PLayer(tv).start(); new Watcher(tv).start(); } } //生產者:演員 class PLayer extends Thread{ public TV tv; public PLayer(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 10; i++) { if(i%2==0) this.tv.play("地球脈動"); else this.tv.play("COSMOS"); } } } //消費者:觀眾 class Watcher extends Thread{ public TV tv; public Watcher(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 10; i++) { this.tv.watch(); } } } //電視機TV class TV { //節目名字 private String programName; //訊號燈標誌位 //演員表演中,觀眾等待 ture //觀眾收看中,演員等待 false private boolean flag = true; //演員表演節目 public synchronized void play(String programName){ if(!flag){ try { //觀眾收看中 演員等待 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //觀眾等待中 演員表演 this.programName = programName; System.out.println("演員表演了-->" + programName); //演員表演完了 this.flag = !this.flag;//訊號燈反轉 //通知喚醒觀眾觀看 this.notifyAll(); } //觀眾收看節目 public synchronized void watch(){ if(flag){ try { //演員表演中 觀眾等待 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //演員等待中 觀眾收看 System.out.println("觀眾收看了-->" + this.programName); //觀眾收看完了 this.flag = !this.flag;//訊號燈反轉 //通知喚醒演員表演 this.notifyAll(); } }
執行緒池
public class Pool_Test {
public static void main(String[] args) {
//1.建立服務,建立執行緒池
ExecutorService service = Executors.newFixedThreadPool(10);//引數為執行緒池大小
//執行
service.execute(new MyThead());
service.execute(new MyThead());
service.execute(new MyThead());
service.execute(new MyThead());
//2.關閉連線
service.shutdown();
}
}
class MyThead implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}