Thinking in java 單個生產者消費者ChefWaitPerson鎖擷取執行程式__java_concurrency

來源:互聯網
上載者:User
1.       Meal類

class Meal {    private final int orderNum;    public Meal(int orderNum) {        this.orderNum = orderNum;    }    public String toString() {        return "Meal " + orderNum;    }}
2.       Chef類
class Chef implements Runnable {    private Restaurant restaurant;    private int count = 0;    public Chef(Restaurant r) {        restaurant = r;    }    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (this) {                    //這個時候可能會跳轉到別的線程                    //慣用法 確保你可以重返等待狀態                    while (restaurant.meal != null)                        //這個時候可能會跳轉到別的線程                        wait(); // ... for the meal to be taken                }                //取消阻塞狀態 執行工作                if (++count == 10) {                    print("Out of food, closing");                    //線上程內部                    restaurant.exec.shutdownNow();                }                printnb("Order up! ");                //對notifyAll()的調用必須首先捕獲waitPerson上的鎖                //因為調用notifyAll()必然擁有這個鎖                //這可以保證兩個試圖在同一個對象上調用notifyAll()的任務不會互相衝突                //當執行到這裡的時候,如果需要等待鎖 那麼線程會進行切換到chef 所以還可以繼續運行下去                synchronized (restaurant.waitPerson) {                    //修改狀態的時候一定要擷取WaitPerson鎖,否則在修改狀態的時候如果線程發生了切換,                    //就會導致錯失訊號(比如先發了notifyAll而之後線程切換到wait)而造成的死結                    restaurant.meal = new Meal(count);                    //此時無論WaitPerson是非阻塞/阻塞狀態 都可以執行                    restaurant.waitPerson.notifyAll();                }                //這個時候可能會跳轉到別的線程 使Chef線程處於阻塞/非阻塞狀態                TimeUnit.MILLISECONDS.sleep(100);            }        } catch (InterruptedException e) {            print("Chef interrupted");        }    }}
3.       WaitPerson類
class WaitPerson implements Runnable {    private Restaurant restaurant;    public WaitPerson(Restaurant r) {        restaurant = r;    }    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (this) {                    while (restaurant.meal == null)                        wait(); // ... for the chef to produce a meal                }                print("Waitperson got " + restaurant.meal);                synchronized (restaurant.chef) {                    restaurant.meal = null;                    restaurant.chef.notifyAll(); // Ready for another                }            }        } catch (InterruptedException e) {            print("WaitPerson interrupted");        }    }}
4.       Restaurant類
//Restaurant是WaitPerson和Chef的焦點,他們都必須知道在為哪個Restaurant工作,//因為他們必須和這家飯店的“餐窗”打交道,以便放置或拿取膳食restaurant.meal .public class Restaurant {    //餐窗的meal    Meal meal;    ExecutorService exec = Executors.newCachedThreadPool();    WaitPerson waitPerson = new WaitPerson(this);    Chef chef = new Chef(this);    //在構造方法中啟動兩個線程    public Restaurant() {        exec.execute(chef);        exec.execute(waitPerson);    }    public static void main(String[] args) {        new Restaurant();    }}

類圖

執行過程:

啟動Chef線程後,

首先擷取自身鎖。此時線程調度器可能會跳到另外的線程 這些在使用synchronized同步代碼塊的時候都要考慮

(可能處於擷取到Chef鎖的狀態,也可能處於掛起後釋放Chef鎖的狀態)

while (!Thread.interrupted()) {    //先擷取Chef對象鎖    //判斷是否要掛起本線程    //掛起線程 形成有一個任務在Chef的鎖上等待的狀況    //不論是否掛起此線程 都釋放此線程對象鎖    synchronized (this) {                ////這個時候可能會跳轉到別的線程                //慣用法 確保你可以重返等待狀態        while (restaurant.meal != null)            ////這個時候可能會跳轉到別的線程            wait(); // ... for the meal to be taken    }

1. 當處於擷取Chef鎖的狀態時,線程調度器執行WaitPerson線程

由於meal==null,所以WaitPerson線程會掛起,釋放WaitPerson鎖

2. 當處於釋放Chef鎖的狀態時,線程調度器執行WaitPerson線程

由於meal==null,所以WaitPerson線程會掛起,釋放WaitPerson鎖

 

所以殊途同歸,還是要回到Chef線程,繼續執行

執行doMeal動作之後

先擷取WaitPerson鎖,WaitPerson鎖存不存在鎖住的情況呢。存在。比如當執行WaitPerson線程時,線程調度器在剛剛進入synchronized(this)同步代碼塊的時候切換線程,此時WaitPerson鎖就被WaitPerson線程擷取著。那麼此時Chef線程就被阻塞了,線程調度器會切換線程,WaitPerson線程繼續執行,鎖會被釋放掉,線程掛起。Chef線程可以繼續執行下去。擷取WaitPerson鎖,修改狀態(修改狀態的時候一定要擷取WaitPerson鎖,否則在修改狀態的時候如果線程發生了切換,就會導致錯失訊號(比如先發了notifyAll而之後線程切換到wait)而造成的死結)。通知WaitPerson線程,釋放WaitPerson鎖。此時WaitPerson無論處於非阻塞還是掛起狀態都可以繼續運行,而WaitPerson此時無論切換還是不切換,是掛起還是非阻塞狀態下切換都可以。

 

通過這個程式的閱讀,就會發現其思路根本不是那麼清晰。之前做過WaxOnWaxOff代碼的閱讀,這兩個程式在思路上有什麼區別呢。

1. WaxOnWaxOff程式 是在一個對象car上反覆執行兩個線程的操作,使用的是car對象鎖。而ChefWaitPerson程式是一個單個的生產者消費者任務,其使用了兩個線程對象鎖,更為複雜。

2. 注意在ChefWaitPerson程式中其線程調度器可以在程式的任何語句處進行線程切換(Synchronized關鍵字不能阻止線程切換,而只能保證其擷取鎖從而多任務順序執行),所以在多種情況下切換仍能保證兩個線程不發生死結的情況,其狀態管理沒有WaxOnWaxOff簡單清晰。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.