[Java] 16.2. 同步化

處理總有先後順序的相依性,這時就必須執行緒間進行同步化。

 

image/svg+xml16.2. 同步化 - synchronized class Company { int cash = 1000 ; void withdraw ( String empName , int amount ){ if ( cash >= amount ){ try { Thread.sleep ( 10 ); } catch ( InterruptedException e){} System.out.println(empName + " 提取了 " + amont + 元。 " ); cash = cash - amount ; } System.out.println( " 剩下的零⽤⾦: " +cash); } } class Employee extends Thread { String name; Company company; Employee( String name, Company company){ this .name = name; this .company = company; } public void run (){ for ( int i= 0 ; i< 3 ; i++) company.withdraw (name, 300 ); } } class Main{ public static void main( String [] args){ Company com = new Company (); Employee e1 = new Employee ( "Jack" , com ); Employee e2 = new Employee ( "Eric" , com ); e1 . start (); e2 . start (); } } 執⾏結果 Jack 提取了 300 元。 剩下的零⽤⾦: 700 Eric 提取了 300 元。 剩下的零⽤⾦: 400 Jack 提取了 300 元。 剩下的零⽤⾦: 100 剩下的零⽤⾦: 100 Eric 提取了 300 元。 剩下的零⽤⾦: -200 剩下的零⽤⾦: -200 class Company { int cash = 1000 ; void withdraw ( String empName, int amount){ if ( cash >= amount ){ try { Thread.sleep ( 10 ); } catch ( InterruptedException e){} System.out.println(empName + “ 提取了 ” + amont + “ 元。 ”); cash = cash - amount ; } System.out.println( " 剩下的零⽤⾦: " +cash); } } class Company { int cash = 1000 ; synchronized void withdraw ( String empName, int amount){ if ( cash >= amount ){ try { Thread.sleep( 10 ); } catch ( InterruptedException e){} System.out.println(empName + " 提取了 " + amount + 元。 " ); cash=cash-amount; } System.out.println( " 剩下的零⽤⾦: " +cash); } } class Main{ public static void main( String [] args){ Company com = new Company(); Employee e1 = new Employee ( "Jack" , com ); Employee e2 = new Employee ( "Eric" , com ); e1 . start (); e2 . start (); } } 執⾏結果 Jack 提取了 300 元。 剩下的零⽤⾦: 700 Jack 提取了 300 元。 剩下的零⽤⾦: 400 Jack 提取了 300 元。 剩下的零⽤⾦: 100 剩下的零⽤⾦: 100 剩下的零⽤⾦: 100 剩下的零⽤⾦: 100 J16_2_1 Company.java 1. 這是公司類別( Company ),其中有個零⽤⾦ 屬性( cash )以存放零⽤⾦,預設為 1000 J16_2_1 Employee.java 3. 加入 if 限制,當無⾜夠零⽤⾦時,即不可進⾏提⽤零⽤⾦的動作。 4. 讓執⾏緒休息⼀下,模擬領⽤零⽤⾦時, 是要花⼀點時間的。 2. 建立提款⽅法( withdraw() ),讓員⼯提過此⽅法進⾏提⽤零⽤⾦的動 作,只要代入員⼯姓名( empName )與領⽤額度( amont )即可提領現⾦。 5. 扣除零⽤⾦的動作發⽣在成功提⽤零⽤⾦的 情況下。記住,這已經是在 if 中的程式了。 7. 改寫 run() ⽅法,讓每位員⼯在執⾏緒開始時, 就會進⾏連3次的提款動作,每次提領 300 元。 8. 建立⼀個公司物件。 6. 員⼯類別( Employee )即承⾃ Thread 類別。 9. 兩個員⼯皆指定同⼀家公司,表⽰此兩位員⼯是同⼀家公司的員⼯, 因此可以同時對此公司進⾏提款( withdraw() )的動作。 10. 兩位員⼯同時透過執⾏緒的能⼒,對公司各提 300 元的零⽤ ⾦3次。但結果怎麼會有得到零⽤⾦被提成負數的情況,不是已經 使⽤ if 來防⽌無⾜夠零⽤⾦的狀況下,是不能提款的嗎? J16_2_1 Main.java J16_2_2 Company.java Jack 員⼯ 執⾏緒 Eric 員⼯ 執⾏緒 1. 這是公司類別( Company )的提取 零⽤⾦⽅法( withdraw() )。 3. Eric 員⼯進⾏提款的動 作,發現還有 400 元的零⽤⾦,因 此也進入 if 進⾏提款的動作。 . 假設⽬前零⽤ ⾦還有 400 Jack 員⼯成功的進入 if ,因提款的動作 需要花 0.001 秒, 因此暫停執⾏緒。 但還未扣除零⽤⾦ 300 元。 4. 單純的透過 if 並不能保障執⾏緒正確的扣除提領的⾦額。最 後因 Jack Eric 皆在零⽤⾦只有 400 元的情況下,成功的進入 if 進⾏提款的動作,因此造成零⽤⾦變成負數的情況發⽣ 1. 這是公司類別( Company ),其中有個零⽤⾦ 屬性( cash )以存放零⽤⾦,預設為 1000 3. 建立 2 個員⼯物件( Employee )以交互顉取零⽤⾦。 4. 不論執⾏多少次,最後零⽤⾦都會剩下 100 元,不會有預⽀的情況發 ⽣,因為 synchronized 確保⼀次只有⼀位員⼯可以呼叫提款⽅法。 2. 當提款⽅法( withdraw() )被使⽤ synchronized 關鍵字進⾏同步化後,員⼯呼叫此提款 ⽅法時,必須以同步化的⽅式進⾏,也就是要排隊使⽤ 的意思。即使是在執⾏緒獨立執⾏的狀況也是⼀樣。 J16_2_2 Main.java 公司提供給員⼯的的零⽤⾦竟然⽤到變成負數了? 到底是怎麼回事? 1 1 1 1 1 原來是員⼯執⾏緒在交換執⾏的過程中, 造成領取零⽤⾦的規則被打破了 synchronized 同步化了零⽤⾦領⽤⽅法, 讓員⼯以排隊的⽅式進⾏零⽤⾦的領⽤ 1 1 1 1

留言