1. 程式人生 > 其它 >Java多執行緒開發 06 —— 管程法、訊號燈法

Java多執行緒開發 06 —— 管程法、訊號燈法

技術標籤:Java學習java多執行緒

文章目錄


生產者、消費者問題

應用場景:

  1. 假設倉庫只能放一件產品,生產者放入一件產品到倉庫,消費者從倉取出一件產品。
  2. 若倉庫有一件產品,則生產者必須等待消費者取出一件。
  3. 若倉庫沒有產品,則消費者必須等待生產者放入一件產品。

這裡就涉及到一個執行緒通訊的問題。

  1. 對於生產者,在生產了一件產品後要通知消費者取走。
  2. 對於消費者,在取走了一件產品後要通知生產者生產。

在這個問題中,僅用synchronized是不夠的,它不能實現不同執行緒之間的訊息傳遞。

Java 提供了幾個方法來解決執行緒之間的通訊問題:

方法名作用
wait()表示執行緒一直等待,直到有其他執行緒通知(與sleep()不同,可以釋放鎖)
wait(long timeout)指定等待的毫秒數
notify()喚醒一個處於等待狀態的執行緒
notifyAll()喚醒同一個物件上所有呼叫wait()方法的執行緒,優先順序高的優先排程

注意:這些都是Object類的方法,只能在同步方法或同步程式碼塊中使用,否則會丟擲異常。

管程法

生產者將生產好的資料放入緩衝區,消費者衝緩衝區中拿出資料,緩衝區同一時刻只能被一個人操作。

package lessen08_Thread;

//測試生產者消費者模型 —— 管程法(利用緩衝區)
public class TestPC01
{ public static void main(String[] args) { Buffer buffer = new Buffer(); new Thread(new Producer(buffer)).start(); new Thread(new Consumer(buffer)).start(); } } //生產者 class Producer implements Runnable{ Buffer buffer = null; public Producer(Buffer buffer) { this
.buffer = buffer; } @Override public void run() { for (int i = 0; i < 100; i++) { buffer.push(new Product(i)); System.out.println("生產——>"+i); } } } //消費者 class Consumer implements Runnable{ Buffer buffer = null; public Consumer(Buffer buffer) { this.buffer = buffer; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消費——>"+buffer.pop().getId()); } } } //產品 class Product{ private int id; public Product(int id) { this.id = id; } public int getId() { return id; } } //緩衝區 class Buffer{ private Product[] products = new Product[10];//存放產品 private int count = 0;//當前緩衝區產品數量 //放入產品[對緩衝區的操作涉及併發操作,可同步方法、同步程式碼塊、lock] public synchronized void push(Product product){ //若緩衝區滿,則生產者需要等待取走 if (count >= products.length){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } products[count++] = product;//生產 //此時生產者生產了產品,通知消費者消費 this.notifyAll(); } //取走產品 public synchronized Product pop() { //若緩衝區空,則消費者需等待生產者生產 if(count <= 0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Product product = products[--count];//消費 //此時消費者已經消費了一個產品,通知生產者生產 this.notifyAll(); return product; } }

部分結果:

生產——>99
消費——>96
消費——>99
消費——>98
消費——>97
消費——>95
消費——>94
消費——>93
消費——>92

訊號燈法

通過標誌位來判斷是產者、消費者是等待還執行。

package lessen08_Thread;

//測試生產者消費者模型 ———— 訊號燈法(利用標誌位)
public class TestPC02 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Thread(new Watcher(tv)).start();
        new Thread(new Player(tv)).start();
    }
}

//生產者————演員
class Player implements Runnable{

    TV tv = null;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            tv.play("節目:"+i);
        }
    }
}

//消費者————觀眾
class Watcher implements Runnable{
    TV tv = null;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            tv.watch();
        }
    }
}

//產品————節目
class TV{
    String program;//表演的節目
    //true 演員表演,觀眾等待
    //false 觀眾觀看,演員等待
    boolean flag = true;

    //表演
    public synchronized void play(String program){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("演員表演了:"+program);
        //演員表演好了,通知觀眾觀看
        this.notifyAll();
        this.program = program;
        this.flag = false;//標誌位反轉

    }

    //觀看
    public synchronized void watch(){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("觀眾觀看了:"+program);
        //觀眾看完了,通知演員表演
        this.notifyAll();
        this.flag = true;
    }
}

部分結果:

演員表演了:節目:0
觀眾觀看了:節目:0
演員表演了:節目:1
觀眾觀看了:節目:1
演員表演了:節目:2
觀眾觀看了:節目:2