[Java] 16.4. 等待 & 通知

不同的執行緒之間是可以互相等待與被通知的,以達到執行緒間的同步化。

 

image/svg+xml16.4. 等待 / 通知 – wait/notify class Company { int cash = 1000 ; } class Employee extends Thread { String name; Company company; Employee( String name, Company company){ this .name = name; this .company=company; } public void run (){ while ( true ){ synchronized ( company ){ if ( company.cash >= 300 ){ company.cash = company.cash - 300 ; System.out.println(name + " 提取了 300 元。餘額為 " +company.cash); } else { company.notify (); try { company.wait (); } catch ( InterruptedException e){} } } } } } class Boss extends Thread { String name; Company company; Boss( String name, Company company){ this .name = name; this .company=company; } public void run (){ while ( true ){ synchronized ( company ){ if ( company.cash < 300 ){ company.cash = company.cash+ 1000 ; System.out.println( " 老闆 " +name + " 補充了 1000 元。餘額為 " + company.cash); company.notify (); } try { company.wait (); } catch ( InterruptedException e){} } } } } class Main{ public static void main( String [] args){ Company com = new Company (); Boss b = new Boss ( "Conrad" , com ); Employee e = new Employee( "Jack" , com ); b.start (); e.start (); } } 部份執⾏結果 Jack 提取了 300 元。餘額為 900 Jack 提取了 300 元。餘額為 600 Jack 提取了 300 元。餘額為 300 Jack 提取了 300 元。餘額為 0 老闆 Conrad 補充了 1000 元。餘額為 1000 Jack 提取了 300 元。餘額為 700 Jack 提取了 300 元。餘額為 400 Jack 提取了 300 元。餘額為 100 老闆 Conrad 補充了 1000 元。餘額為 1100 Jack 提取了 300 元。餘額為 800 Jack 提取了 300 元。餘額為 500 Jack 提取了 300 元。餘額為 200 class Company { int cash = 1000 ; } class Employee extends Thread { String name; Company company; Employee( String name, Company company){ this .name = name; this .company=company; } public void run (){ while ( true ){ synchronized ( company ){ if ( company.cash >= 300 ){ company.cash = company.cash - 300 ; System.out.println(name + " 提取了 300 元。餘額為 " +company.cash); } else { company.notifyAll (); try { company.wait (); } catch ( InterruptedException e){} } } } } } class Boss extends Thread { String name; Company company; Boss( String name, Company company){ this .name = name; this .company=company; } public void run (){ while ( true ){ synchronized ( company ){ if ( company.cash < 300 ){ company.cash = company.cash+ 1000 ; System.out.println( " 老闆 " +name + " 補充了 1000 元。餘額為 " + company.cash); company.notify (); } try { company.wait (); } catch ( InterruptedException e){} } } } } class MainClass{ public static void main( String [] args){ Company com = new Company (); Boss b1 = new Boss ( "John" , com ); Boss b2 = new Boss ( "Conrad" , com ); Employee e1 = new Employee ( "Jack" , com ); Employee e2 = new Employee ( "Mary" , com ); Employee e3 = new Employee ( "Eric" , com ); b1 . start (); b2 . start (); e1 . start (); e2 . start (); e3 . start (); } } 部份執⾏結果 老闆 Conrad 補充了 1000 元。餘額為 1000 Mary 提取了 300 元。餘額為 700 Mary 提取了 300 元。餘額為 400 Jack 提取了 300 元。餘額為 100 老闆 John 補充了 1000 元。餘額為 1100 Jack 提取了 300 元。餘額為 800 Jack 提取了 300 元。餘額為 500 Jack 提取了 300 元。餘額為 200 老闆 Conrad 補充了 1000 元。餘額為 1200 J16_4_1 Company.java 只剩下 200 元,員⼯已經不能在提領零⽤⾦了, 怎麼讓老闆主動補充零⽤⾦呢? J16_4_1 Employee.java 1. 現在公司類別( Company )只有⼀個簡單的零⽤⾦屬性( cash )。 2. 員⼯類別( Employee )繼承⾃ Thread 類別。 3. 改寫 run() ⽅法,並執⾏無限迴圈,讓員⼯⼀再的領取零⽤⾦。 4. 以公司物件作為同步化的物件鎖。 5. 員⼯只有在零⽤⾦⼤於等於 300 時才進⾏提領零⽤⾦的動作。 6. 若零⽤⾦不⾜時,則呼叫物件鎖上的 notify() ⽅法,通知其他執⾏緒不必在等 待,可以試著取得物件鎖,以執⾏同步化 區塊中的程式。在此範例通知的對象即是 老闆執⾏緒,以進⾏零⽤⾦補充的動作。 7. 因零⽤⾦的不⾜,因此呼叫物件鎖上的 wait() ⽅法進入等待的 狀態,此時正在使⽤的物件鎖會暫時的被放棄,以供其他執⾏緒使 ⽤。當然程式也會停留在 wait() ⽅法這⼀⾏,直到其他執⾏緒透過 notify() ⽅法,通知不需要在進⾏等待的動作,這時才會嘗試取 得物件鎖(不⼀定⾺上取得),以繼續執⾏程式。 J16_4_1 Boss.java 8. 老闆類別也是繼承⾃ Thread 類別。 9. 執⾏無限迴圈,讓老闆 ⼀再的補充零⽤⾦。 10. 同樣的,以公司物件作為同步化的物件鎖。 11. 老闆只有在零⽤⾦⼩於 300 時才進⾏補充零⽤⾦的動作。 12. 在補充元零⽤⾦後,呼 叫物件鎖上的 notify() 法通知其他執⾏緒不必在等 待。在此範例通知的對象即 是員⼯執⾏緒,以進⾏領取 零⽤⾦的動作。 13. 當補充完零⽤⾦後,即呼叫物件鎖上的 wait() ⽅法進入等待的狀態,此時 正在使⽤的物件鎖會暫時的被放棄,以供其他執⾏緒使⽤。當然程式也會停留在 wait() ⽅法這⼀⾏,直到其他執⾏緒透過 notify() ⽅法,通知不需要在進⾏ 等待的動作,這時才會嘗試取得物件鎖(不⼀定⾺上取得),以繼續執⾏程式。 J16_4_1 Main.java 14. 建立⼀個公司物件。 15. 老闆與員⼯同屬於同⼀家公司,因此會以此公司作為物件鎖。 16. 啟動老闆與員⼯執⾏緒,讓員⼯持續領取零⽤⾦, 並在不⾜時透過老闆執⾏緒補充零⽤⾦。 17. 在整個執⾏過程式,員⼯ Jack 會在不⾜ 300 元的情況下停⽌提領零 ⽤⾦的動作,並通知老闆 Conrad 進⾏補充零⽤⾦的動作;⽽當老闆 Conrad 補充完零⽤⾦後,即會停⽌補充零⽤⾦,並通知員⼯ Jack 可以進 ⾏零⽤⾦的提取動作。整個程式會持續以這種模式進⾏下去。 J16_4_2 Company.java J16_4_2 Employee.java J16_4_2 Boss.java J16_4_2 Main.java 1. 現在公司類別( Company )只有⼀個簡單的零⽤⾦屬性( cash )。 2. 員⼯類別( Employee )繼承⾃ Thread 類別。 3. 改寫 run() ⽅法,並執⾏無限迴圈,讓員⼯⼀再的領取零⽤⾦。 4. 以公司物件作為同步化的物件鎖。 5. 員⼯只有在零⽤⾦⼤於等於 300 時才進⾏提領零⽤⾦的動作。 . 在零⽤⾦不⾜時,使⽤ notifyAll() ⽅法⼀次通知所有其他的執 ⾏緒-老闆執⾏緒(但也有可能是員⼯執⾏緒)不必在等待了,可以嘗 試取得物件鎖,執⾏同步化區塊中的程式,以補充零⽤⾦。 7. 同樣的在零⽤⾦的不⾜,呼叫物件 鎖上的 wait() ⽅法進入等待的狀態。 8. 老闆類別也是繼承⾃ Thread 類別。 9. 執⾏無限迴圈,讓老闆 ⼀再的補充零⽤⾦。 10. 同樣的,以公司物件作為同步化的物件鎖。 11. 老闆只有在零⽤⾦⼩於 300 時才進⾏補充零⽤⾦的動作。 12. 老闆在補充完零⽤ ⾦後,呼叫 notifyAll() ⽅法⼀次 通知所有其他的執⾏緒- 員⼯執⾏緒(但也有可能 是老闆執⾏緒),不必在 等待了,可以嘗試取得物 件鎖,執⾏同步化區塊中 的程式,以領取零⽤⾦。 13. 同樣的當補充完零⽤⾦後,即呼叫物 件鎖上的 wait() ⽅法進入等待的狀態。 14. 建立⼀個公司物件。 15. 老闆與員⼯同屬於同⼀家公司,因此會以此公司作為物件鎖。 16. 啟動老闆與員⼯執⾏緒,讓員⼯持續領取零⽤⾦, 並在不⾜時透過老闆執⾏緒補充零⽤⾦。 17. 多位老闆與員⼯執⾏緒,皆能夠以同步化區塊達到同步化的效果。雖 然使⽤的是物件貰上的 notifyAll() ⽅法,⼀次通知所有等待中的執⾏緒 不必在等待了,但因為所有的老闆與員⼯執⾏緒還是要在取得物件鎖的情 況下才能夠執⾏同步化區塊中的程式,因此也還是會達到同步化的效果, 並不會因為使⽤ notifyAll() ⽅法⽽造成非同步化的問題。 如何讓員⼯在有⾜夠零⽤⾦時進⾏零⽤⾦的領取,且在零⽤ ⾦不⾜時,老闆必須被通知以進⾏零⽤⾦的補充? wait()/notify() ,讓老闆可以在沒有零⽤⾦時添加零⽤⾦, 並通知員⼯零⽤⾦已經補滿了 1 2 1 2 2 2 1 notifyAll() ,讓老闆可以⼀次通知多名員⼯零⽤⾦已補滿的消 息,當然員⼯也可以通知多位老闆補充零⽤⾦ 1 2 3 1 2 2 4 5 3 3 4

留言