posts - 40,  comments - 7,  trackbacks - 0

          Java語言內置了synchronized關鍵字用于對多線程進行同步,大大方便了Java中多線程程序的編寫。但是僅僅使用synchronized關鍵字還不能滿足對多線程進行同步的所有需要。大家知道,synchronized僅僅能夠對方法或者代碼塊進行同步,如果我們一個應用需要跨越多個方法進行同步,synchroinzed就不能勝任了。在C++中有很多同步機制,比如信號量、互斥體、臨屆區等。在Java中也可以在synchronized語言特性的基礎上,在更高層次構建這樣的同步工具,以方便我們的使用。
          ????當前,廣為使用的是由Doug?Lea編寫的一個Java中同步的工具包,可以在這兒了解更多這個包的詳細情況:
          ????http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
          ????該工具包已經作為JSR166正處于JCP的控制下,即將作為JDK1.5的正式組成部分。本文并不打算詳細剖析這個工具包,而是對多種同步機制的一個介紹,同時給出這類同步機制的實例實現,這并不是工業級的實現。但其中會參考Doug?Lea的這個同步包中的工業級實現的一些代碼片斷。
          ????本例中還沿用上篇中的Account類,不過我們這兒編寫一個新的ATM類來模擬自動提款機,通過一個ATMTester的類,生成10個ATM線程,同時對John賬戶進行查詢、提款和存款操作。Account類做了一些改動,以便適應本篇的需要:

          1. import ?java.util.HashMap;
          2. import ?java.util.Map;
          3. class ?Account?{
          4. ????String?name;
          5. ????//float?amount;
          6. ????
          7. ????//使用一個Map模擬持久存儲
          8. ????static?Map?storage?=?new?HashMap();
          9. ????static?{
          10. ????????storage.put("John",?new?Float(1000.0f));
          11. ????????storage.put("Mike",?new?Float(800.0f));
          12. ????}????
          13. ????
          14. ????
          15. ????public?Account(String?name)?{
          16. ????????//System.out.println("new?account:"?+?name);
          17. ????????this.name?=?name;
          18. ????????//this.amount?=?((Float)storage.get(name)).floatValue();
          19. ????}
          20. ????public?synchronized?void?deposit(float?amt)?{
          21. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          22. ????????storage.put(name,?new?Float(amount?+?amt));
          23. ????}
          24. ????public?synchronized?void?withdraw(float?amt)?throws?InsufficientBalanceException?{
          25. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          26. ????????if?(amount?>=?amt)
          27. ????????????amount?-=?amt;
          28. ????????else?
          29. ????????????throw?new?InsufficientBalanceException();
          30. ????????????????
          31. ????????storage.put(name,?new?Float(amount));
          32. ????}
          33. ????public?float?getBalance()?{
          34. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          35. ????????return?amount;
          36. ????}
          37. }



          在新的Account類中,我們采用一個HashMap來存儲賬戶信息。Account由ATM類通過login登錄后使用:

          1. public ?class?ATM?{
          2. ????Account?acc;
          3. ????
          4. ????//作為演示,省略了密碼驗證
          5. ????public?boolean?login(String?name)?{
          6. ????????if?(acc?!=?null)
          7. ????????????throw?new?IllegalArgumentException("Already?logged?in!");
          8. ????????acc?=?new?Account(name);
          9. ????????return?true;
          10. ????}
          11. ????
          12. ????public?void?deposit(float?amt)?{
          13. ????????acc.deposit(amt);
          14. ????}
          15. ????
          16. ????public?void?withdraw(float?amt)?throws?InsufficientBalanceException??{
          17. ????????????acc.withdraw(amt);
          18. ????}
          19. ????
          20. ????public?float?getBalance()?{
          21. ????????return?acc.getBalance();
          22. ????}
          23. ????
          24. ????public?void?logout?()?{
          25. ????????acc?=?null;
          26. ????}
          27. ????
          28. }
          29. 下面是ATMTester,在ATMTester中首先生成了10個ATM實例,然后啟動10個線程,同時登錄John的賬戶,先查詢余額,然后,再提取余額的80%,然后再存入等額的款(以維持最終的余額的不變)。按照我們的預想,應該不會發生金額不足的問題。首先看代碼:

            1. public ?class?ATMTester?{
            2. ????private?static?final?int?NUM_OF_ATM?=?10;
            3. ????public?static?void?main(String[]?args)?{
            4. ????????ATMTester?tester?=?new?ATMTester();
            5. ????????
            6. ????????final?Thread?thread[]?=?new?Thread[NUM_OF_ATM];
            7. ????????final?ATM?atm[]?=?new?ATM[NUM_OF_ATM];
            8. ????????for?(int?i=0;?i
            9. ????????????atm[i]?=?new?ATM();
            10. ????????????thread[i]?=?new?Thread(tester.new?Runner(atm[i]));
            11. ????????????thread[i].start();
            12. ????????}????
            13. ????????
            14. ????}
            15. ????
            16. ????class?Runner?implements?Runnable?{
            17. ????????ATM?atm;
            18. ????????
            19. ????????Runner(ATM?atm)?{
            20. ????????????this.atm?=?atm;
            21. ????????}
            22. ????????
            23. ????????public?void?run()?{
            24. ????????????atm.login("John");
            25. ????????????//查詢余額
            26. ????????????float?bal?=?atm.getBalance();
            27. ????????????try?{
            28. ????????????????Thread.sleep(1);?//模擬人從查詢到取款之間的間隔
            29. ????????????}?catch?(InterruptedException?e)?{
            30. ????????????????//?ignore?it
            31. ????????????}?
            32. ????????????
            33. ????????????try?{
            34. ????????????????System.out.println("Your?balance?is:"?+?bal);
            35. ????????????????System.out.println("withdraw:"?+?bal?*?0.8f);
            36. ????????????????atm.withdraw(bal?*?0.8f);
            37. ????????????????System.out.println("deposit:"?+?bal?*?0.8f);
            38. ????????????????atm.deposit(bal?*?0.8f);
            39. ????????????}?catch?(InsufficientBalanceException?e1)?{
            40. ????????????????System.out.println("余額不足!");
            41. ????????????}?finally?{
            42. ????????????????????????????????????atm.logout();
            43. ???????????????????????????}
            44. ????????}
            45. ????}
            46. }


            運行ATMTester,結果如下(每次運行結果都有所差異):

            Your?balance?is:1000.0
            withdraw:800.0
            deposit:800.0
            Your?balance?is:1000.0
            Your?balance?is:1000.0
            withdraw:800.0
            withdraw:800.0
            余額不足!
            Your?balance?is:200.0
            Your?balance?is:200.0
            Your?balance?is:200.0
            余額不足!
            Your?balance?is:200.0
            Your?balance?is:200.0
            Your?balance?is:200.0
            Your?balance?is:200.0
            withdraw:160.0
            withdraw:160.0
            withdraw:160.0
            withdraw:160.0
            withdraw:160.0
            withdraw:160.0
            withdraw:160.0
            deposit:160.0
            余額不足!
            余額不足!
            余額不足!
            余額不足!
            余額不足!
            余額不足!

            為什么會出現這樣的情況?因為我們這兒是多個ATM同時對同一賬戶進行操作,比如一個ATM查詢出了余額為1000,第二個ATM也查詢出了余額1000,然后兩者都期望提取出800,那么只有第1個用戶能夠成功提出,因為在第1個提出800后,賬戶真實的余額就只有200了,而第二個用戶仍認為余額為1000。這個問題是由于多個ATM同時對同一個賬戶進行操作所不可避免產生的后果。要解決這個問題,就必須限制同一個賬戶在某一時刻,只能由一個ATM進行操作。如何才能做到這一點?直接通過synchronized關鍵字可以嗎?非常遺憾!因為我們現在需要對整個Account的多個方法進行同步,這是跨越多個方法的,而synchronized僅能對方法或者代碼塊進行同步。在下一篇我們將通過編寫一個鎖對象達到這個目的。

          我們首先開發一個BusyFlag的類,類似于C++中的Simaphore。

          1. public ?class?BusyFlag?{
          2. ????protected?Thread?busyflag?=?null;
          3. ????protected?int?busycount?=?0;
          4. ????
          5. ????public?synchronized?void?getBusyFlag()?{
          6. ????????while?(tryGetBusyFlag()?==?false)?{
          7. ????????????try?{
          8. ????????????????wait();
          9. ????????????}?catch?(Exception?e)?{}????????????
          10. ????????}
          11. ????}
          12. ????
          13. ????private?synchronized?boolean?tryGetBusyFlag()?{
          14. ????????if?(busyflag?==?null)?{
          15. ????????????busyflag?=?Thread.currentThread();
          16. ????????????busycount?=?1;
          17. ????????????return?true;
          18. ????????}
          19. ????????
          20. ????????if?(busyflag?==?Thread.currentThread())?{
          21. ????????????busycount++;
          22. ????????????return?true;
          23. ????????}
          24. ????????return?false;????????
          25. ????}
          26. ????
          27. ????public?synchronized?void?freeBusyFlag()?{
          28. ????????if(getOwner()==?Thread.currentThread())?{
          29. ????????????busycount--;
          30. ????????????if(busycount==0)?{
          31. ????????????????busyflag?=?null;
          32. ?????????????????????????????????????notify();
          33. ????????????????????????????}
          34. ????????}
          35. ????}
          36. ????
          37. ????public?synchronized?Thread?getOwner()?{
          38. ????????return?busyflag;
          39. ????}
          40. }


          注:參考Scott?Oaks?&?Henry?Wong《Java?Thread》

          BusyFlag有3個公開方法:getBusyFlag,?freeBusyFlag,?getOwner,分別用于獲取忙標志、釋放忙標志和獲取當前占用忙標志的線程。使用這個BusyFlag也非常地簡單,只需要在需要鎖定的地方,調用BusyFlag的getBusyFlag(),在對鎖定的資源使用完畢時,再調用改BusyFlag的freeBusyFlag()即可。下面我們開始改造上篇中的Account和ATM類,并應用BusyFlag工具類使得同時只有一個線程能夠訪問同一個賬戶的目標得以實現。首先,要改造Account類,在Account中內置了一個BusyFlag對象,并通過此標志對象對Account進行鎖定和解鎖:

          1. import ?java.util.Collections;
          2. import ?java.util.HashMap;
          3. import ?java.util.Map;
          4. class ?Account?{
          5. ????String?name;
          6. ????//float?amount;
          7. ????
          8. ????BusyFlag?flag?=?new?BusyFlag();
          9. ????
          10. ????//使用一個Map模擬持久存儲
          11. ????static?Map?storage?=?new?HashMap();
          12. ????static?{
          13. ????????storage.put("John",?new?Float(1000.0f));
          14. ????????storage.put("Mike",?new?Float(800.0f));
          15. ????}
          16. ????
          17. ????static?Map?accounts?=?Collections.synchronizedMap(new?HashMap());????
          18. ????
          19. ????
          20. ????private?Account(String?name)?{
          21. ????????this.name?=?name;
          22. ????????//this.amount?=?((Float)storage.get(name)).floatValue();
          23. ????}
          24. ????
          25. ????public?synchronized?static?Account?getAccount?(String?name)?{
          26. ????????if?(accounts.get(name)?==?null)
          27. ????????????accounts.put(name,?new?Account(name));
          28. ????????return?(Account)?accounts.get(name);
          29. ????}
          30. ????public?synchronized?void?deposit(float?amt)?{
          31. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          32. ????????storage.put(name,?new?Float(amount?+?amt));
          33. ????}
          34. ????public?synchronized?void?withdraw(float?amt)?throws?InsufficientBalanceException?{
          35. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          36. ????????if?(amount?>=?amt)
          37. ????????????amount?-=?amt;
          38. ????????else?
          39. ????????????throw?new?InsufficientBalanceException();
          40. ????????????????
          41. ????????storage.put(name,?new?Float(amount));
          42. ????}
          43. ????public?float?getBalance()?{
          44. ????????float?amount?=?((Float)storage.get(name)).floatValue();
          45. ????????return?amount;
          46. ????}
          47. ????
          48. ????public?void?lock()?{
          49. ????????flag.getBusyFlag();
          50. ????}
          51. ????
          52. ????public?void?unlock()?{
          53. ????????flag.freeBusyFlag();
          54. ????}
          55. }

          新的Account提供了兩個用于鎖定的方法:lock()和unlock(),供Account對象的客戶端在需要時鎖定Account和解鎖Account,Account通過委托給BusyFlag來提供這個機制。另外,大家也發現了,新的Account中提供了對Account對象的緩存,同時去除了public的構造方法,改為使用一個靜態工廠方法供用戶獲取Account的實例,這樣做也是有必要的,因為我們希望所有的ATM機同時只能有一個能夠對同一個Account進行操作,我們在Account上的鎖定是對一個特定Account對象進行加鎖,如果多個ATM同時實例化多個同一個user的Account對象,那么仍然可以同時操作同一個賬戶。所以,要使用這種機制就必須保證Account對象在系統中的唯一性,所以,這兒使用一個Account的緩存,并將Account的構造方法變為私有的。你也可以說,通過在Account類鎖上進行同步,即將Account中的BusyFlag對象聲明為static的,但這樣就使同時只能有一臺ATM機進行操作了。這樣,在一臺ATM機在操作時,全市其它的所有的ATM機都必須等待。
          另外必須注意的一點是:Account中的getAccount()方法必須同步,否則,將有可能生成多個Account對象,因為可能多個線程同時到達這個方法,并監測到accounts中沒有“John”的Account實例,從而實例化多個John的Account實例。s

          ATM類只需作少量改動,在login方法中鎖定Account,在logout方法中解鎖:

          1. public ?class?ATM?{
          2. ????Account?acc;
          3. ????
          4. ????//作為演示,省略了密碼驗證
          5. ????public?synchronized?boolean?login(String?name)?{
          6. ????????if?(acc?!=?null)
          7. ????????????throw?new?IllegalArgumentException("Already?logged?in!");
          8. ????????acc?=?Account.getAccount(name);
          9. ????????acc.lock();
          10. ????????return?true;
          11. ????}
          12. ????
          13. ????public?void?deposit(float?amt)?{
          14. ????????acc.deposit(amt);
          15. ????}
          16. ????
          17. ????public?void?withdraw(float?amt)?throws?InsufficientBalanceException??{
          18. ????????????acc.withdraw(amt);
          19. ????}
          20. ????
          21. ????public?float?getBalance()?{
          22. ????????return?acc.getBalance();
          23. ????}
          24. ????
          25. ????public?synchronized?void?logout?()?{
          26. ????????acc.unlock();
          27. ????????acc?=?null;
          28. ????}
          29. ????
          30. }



          ATMTester類不需要做任何修改即可同樣運行,同時保證同一個Account同時只能由一個ATM進行操作。解決了上篇提到的多個ATM同時對同一個Account進行操作造成的問題。

          在最新的Doug?Lea的util.concurrent工具包中(現處于JSR166)提供了類似的并發實用類:ReentrantLock,它實現了java?.util.concurrent.locks.Lock接口(將在JDK1.5中發布),它的作用也類似于我們這兒的BusyFlag,實現機制、使用方法也相似。但這是一個工業強度的可重入鎖的實現類。在ReentrantLock的API文檔中有它的使用示例:

          1. ?????Lock?l?=?...;?
          2. ?????l.lock();
          3. ?????try?{
          4. ?????????//?access?the?resource?protected?by?this?lock
          5. ?????}?finally?{
          6. ?????????l.unlock();
          7. ?????}
          posted on 2006-08-17 19:30 Lansing 閱讀(494) 評論(0)  編輯  收藏 所屬分類: Java
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          歡迎探討,努力學習Java哈

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          Lansing's Download

          Lansing's Link

          我的博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 威信县| 玉门市| 太仆寺旗| 铅山县| 罗平县| 娄底市| 兰考县| 安庆市| 宜丰县| 双牌县| 清镇市| 石阡县| 敦煌市| 喀喇| 丰县| 石狮市| 奉贤区| 龙口市| 平度市| 呼玛县| 新泰市| 诏安县| 涪陵区| 乌什县| 福泉市| 尉氏县| 高平市| 苍溪县| 池州市| 会泽县| 通海县| 宁波市| 瑞丽市| 中牟县| 马龙县| 博客| 襄垣县| 天长市| 灵川县| 水城县| 甘泉县|