Java學(xué)習

          java,spring,structs,hibernate,jsf,ireport,jfreechart,jasperreport,tomcat,jboss -----本博客已經(jīng)搬家了,新的地址是 http://www.javaly.cn 如果有對文章有任何疑問或者有任何不懂的地方,歡迎到www.javaly.cn (Java樂園)指出,我會盡力幫助解決。一起進步

           

          多線程問題及處理2

          如果這個例子還不能幫助你理解如何解決多線程的問題,那么下面再來看一個更加實際的例子——衛(wèi)生間問題。

                   例 如火車上車廂的衛(wèi)生間,為了簡單,這里只模擬一個衛(wèi)生間,這個衛(wèi)生間會被多個人同時使用,在實際使用時,當一個人進入衛(wèi)生間時則會把衛(wèi)生間鎖上,等出來時 打開門,下一個人進去把門鎖上,如果有一個人在衛(wèi)生間內(nèi)部則別人的人發(fā)現(xiàn)門是鎖的則只能在外面等待。從編程的角度來看,這里的每個人都可以看作是一個線程 對象,而這個衛(wèi)生間對象由于被多個線程訪問,則就是臨界資源,在一個線程實際使用時,使用synchronized關(guān)鍵將臨界資源鎖定,當結(jié)束時,釋放鎖定。實現(xiàn)的代碼如下:

                            package syn3;

          /**

           * 測試類

           */

          public class TestHuman {

                   public static void main(String[] args) {

                             Toilet t = new Toilet(); //衛(wèi)生間對象

                             Human h1 = new Human("1",t);

                             Human h2 = new Human("2",t);

                             Human h3 = new Human("3",t);

                   }

          }

          package syn3;

          /**

           * 人線程類,演示互斥

           */

          public class Human extends Thread {

                   Toilet t;

                   String name;

                   public Human(String name,Toilet t){

                             this.name = name;

                             this.t = t;

                             start(); //啟動線程

                   }

                  

                   public void run(){

                             //進入衛(wèi)生間

                             t.enter(name);

                   }

          }

          package syn3;

          /**

           * 衛(wèi)生間,互斥的演示

           */

          public class Toilet {

                   public synchronized void enter(String name){

                             System.out.println(name + "已進入!");

                             try{

                                      Thread.sleep(2000);

                             }catch(Exception e){}

                             System.out.println(name + "離開!");

                   }

          }

                   該示例的執(zhí)行結(jié)果為,不同次數(shù)下執(zhí)行結(jié)果會有所不同:

                             1已進入!

          1離開!

          3已進入!

          3離開!

          2已進入!

          2離開!

                   在該示例代碼中,Toilet類表示衛(wèi)生間類,Human類模擬人,是該示例中的線程類,TestHuman類是測試類,用于啟動線程。在TestHuman中,首先創(chuàng)建一個Toilet類型的對象t,并將該對象傳遞到后續(xù)創(chuàng)建的線程對象中,這樣后續(xù)的線程對象就使用同一個Toilet對象,該對象就成為了臨界資源。下面創(chuàng)建了三個Human類型的線程對象,每個線程具有自己的名稱name參數(shù),模擬3個線程,在每個線程對象中,只是調(diào)用對象t中的enter方法,模擬進入衛(wèi)生間的動作,在enter方法中,在進入時輸出調(diào)用該方法的線程進入,然后延遲2秒,輸出該線程離開,然后后續(xù)的一個線程進入,直到三個線程都完成enter方法則程序結(jié)束。

                   在該示例中,同一個Toilet類的對象tenter方法由于具有synchronized修飾符修飾,則在多個線程同時調(diào)用該方法時,如果一個線程進入到enter方法內(nèi)部,則為對象t上鎖,直到enter方法結(jié)束以后釋放對該對象的鎖定,通過這種方式實現(xiàn)無論多少個Human類型的線程,對于同一個對象t,任何時候只能有一個線程執(zhí)行enter方法,這就是解決多線程問題的第一種思路——互斥的解決原理。

          12.4.2 同步

                   使用互斥解決多線程問題是一種簡單有效的解決辦法,但是由于該方法比較簡單,所以只能解決一些基本的問題,對于復(fù)雜的問題就無法解決了。

                   解 決多線程問題的另外一種思路是同步。同步是另外一種解決問題的思路,結(jié)合前面衛(wèi)生間的示例,互斥方式解決多線程的原理是,當一個人進入到衛(wèi)生間內(nèi)部時,別 的人只能在外部時刻等待,這樣就相當于別的人雖然沒有事情做,但是還是要占用別的人的時間,浪費系統(tǒng)的執(zhí)行資源。而同步解決問題的原理是,如果一個人進入 到衛(wèi)生間內(nèi)部時,則別的人可以去睡覺,不占用系統(tǒng)資源,而當這個人從衛(wèi)生間出來以后,把這個睡覺的人叫醒,則它就可以使用臨界資源了。所以使用同步的思路 解決多線程問題更加有效,更加節(jié)約系統(tǒng)的資源。

                   在常見的多線程問題解決中,同步問題的典型示例是“生產(chǎn)者-消費者”模型,也就是生產(chǎn)者線程只負責生產(chǎn),消費者線程只負責消費,在消費者發(fā)現(xiàn)無內(nèi)容可消費時則睡覺。下面舉一個比較實際的例子——生活費問題。

                   生 活費問題是這樣的:學(xué)生每月都需要生活費,家長一次預(yù)存一段時間的生活費,家長和學(xué)生使用統(tǒng)一的一個帳號,在學(xué)生每次取帳號中一部分錢,直到帳號中沒錢時 通知家長存錢,而家長看到帳戶還有錢則不存錢,直到帳戶沒錢時才存錢。在這個例子中,這個帳號被學(xué)生和家長兩個線程同時訪問,則帳號就是臨界資源,兩個線 程是同時執(zhí)行的,當每個線程發(fā)現(xiàn)不符合要求時則等待,并釋放分配給自己的CPU執(zhí)行時間,也就是不占用系統(tǒng)資源。實現(xiàn)該示例的代碼為:

                             package syn4;

          /**

           * 測試類

           */

          public class TestAccount {

                   public static void main(String[] args) {

                             Accout a = new Accout();

                             StudentThread s = new StudentThread(a);

                             GenearchThread g = new GenearchThread(a);

                   }

          }

          package syn4;

          /**

           * 模擬學(xué)生線程

           */

          public class StudentThread extends Thread {

                   Accout a;

                   public StudentThread(Accout a){

                             this.a = a;

                             start();

                   }

                   public void run(){

                             try{

                                      while(true){

                                               Thread.sleep(2000);

                                               a.getMoney(); //取錢

                                      }

                             }catch(Exception e){}

                   }

          }

          package syn4;

          /**

           * 家長線程

           */

          public class GenearchThread extends Thread {

                   Accout a;

                   public GenearchThread(Accout a){

                             this.a = a;

                             start();

                   }

                   public void run(){

                             try{

                                      while(true){

                                               Thread.sleep(12000);

                                               a.saveMoney(); //存錢

                                      }

                             }catch(Exception e){}

                   }

          }

          package syn4;

          /**

           * 銀行賬戶

           */

          public class Accout {

                   int money = 0;

                   /**

                    * 取錢

                    * 如果賬戶沒錢則等待,否則取出所有錢提醒存錢

                    */

                   public synchronized void getMoney(){

                             System.out.println("準備取錢!");

                             try{

                                      if(money == 0){

                                               wait(); //等待

                                      }

                                      //取所有錢

                                      System.out.println("剩余:" + money);

                                      money -= 50;

                                      //提醒存錢

                                      notify();

                             }catch(Exception e){}                

                   }

                  

                   /**

                    * 存錢

                    * 如果有錢則等待,否則存入200提醒取錢

                    */

                   public synchronized void saveMoney(){

                             System.out.println("準備存錢!");

                             try{

                                      if(money != 0){

                                               wait(); //等待

                                      }

                                      //取所有錢

                                      money = 200;

                                      System.out.println("存入:" + money);

                                      //提醒存錢

                                      notify();

                             }catch(Exception e){}                

                   }

          }

                   該程序的一部分執(zhí)行結(jié)果為:

                             準備取錢!

          準備存錢!

          存入:200

          剩余:200

          準備取錢!

          剩余:150

          準備取錢!

          剩余:100

          準備取錢!

          剩余:50

          準備取錢!

          準備存錢!

          存入:200

          剩余:200

          準備取錢!

          剩余:150

          準備取錢!

          剩余:100

          準備取錢!

          剩余:50

          準備取錢!

                   在該示例代碼中,TestAccount類是測試類,主要實現(xiàn)創(chuàng)建帳戶Account類的對象,以及啟動學(xué)生線程StudentThread和啟動家長線程GenearchThread。在StudentThread線程中,執(zhí)行的功能是每隔2秒中取一次錢,每次取50元。在GenearchThread線程中,執(zhí)行的功能是每隔12秒存一次錢,每次存200。這樣存款和取款之間不僅時間間隔存在差異,而且數(shù)量上也會出現(xiàn)交叉。而該示例中,最核心的代碼是Account類的實現(xiàn)。

                   Account類中,實現(xiàn)了同步控制功能,在該類中包含一個關(guān)鍵的屬性money,該屬性的作用是存儲帳戶金額。在介紹該類的實現(xiàn)前,首先介紹一下兩個同步方法——waitnotify方法的使用,這兩個方法都是Object類中的方法,也就是說每個類都包含這兩個方法,換句話說,就是Java天生就支持同步處理。這兩個方法都只能在synchronized修飾的方法或語句塊內(nèi)部采用被調(diào)用。其中wait方法的作用是使調(diào)用該方法的線程休眠,也就是使該線程退出CPU的等待隊列,處于冬眠狀態(tài),不執(zhí)行動作,也不占用CPU排隊的時間,notify方法的作用是喚醒一個因為該對象的線程,該線程當前處于休眠狀態(tài),至于喚醒的具體是那個則不保證。在Account類中,被StudentThread調(diào)用的getMoney方法的功能是判斷當前金額是否是0,如果是則使StudentThread線程處于休眠狀態(tài),如果金額不是0,則取出50元,同時喚醒使用該帳戶對象的其它一個線程,而被GenearchThread線程調(diào)用的saveMoney方法的功能是判斷當前是否不為0,如果是則使GenearchThread線程處于休眠狀態(tài),如果金額是0,則存入200元,同時喚醒使用該帳戶對象的其它一個線程。

                   如果還是不清楚,那就結(jié)合前面的程序執(zhí)行結(jié)果來解釋一下程序執(zhí)行的過程:在程序開始執(zhí)行時,學(xué)生線程和家長線程都啟動起來,所以輸出“準備取錢”和“準備存錢”,然后學(xué)生線程按照該線程run方法的邏輯執(zhí)行,先延遲2秒,然后調(diào)用帳戶對象a中的getMoney方法,但是由于初始情況下帳戶對象a中的money數(shù)值為0,所以學(xué)生線程就休眠了。在學(xué)生線程執(zhí)行的同時,家長線程也按照該線程的run方法的邏輯執(zhí)行,先延遲12秒,然后調(diào)用帳戶對象a中的saveMoney方法,由于帳戶a對象中的money為零,條件不成立,所以執(zhí)行存入200元,同時喚醒線程,由于使用對象a的線程現(xiàn)在只有學(xué)生線程,所以學(xué)生線程被喚醒,開始執(zhí)行邏輯,取出50元,然后喚醒線程,由于當前沒有線程處于休眠狀態(tài),所以沒有線程被喚醒。同時家長線程繼續(xù)執(zhí)行,先延遲12秒,這個時候?qū)W生線程執(zhí)行了4次,耗時4X2=8秒,就取光了帳戶中的錢,接著由于帳戶為0則學(xué)生線程又休眠了,一直到家長線程延遲12秒結(jié)束以后,判斷帳戶為0,又存入了200元,程序繼續(xù)執(zhí)行下去。

                   在解決多線程問題是,互斥和同步都是解決問題的思路,如果需要形象的比較這兩種方式的區(qū)別的話,就看一下下面的示例。一個比較忙的老總,桌子上有2部電話,在一部處于通話狀態(tài)時,另一部響了,老總拿其這部電話說我在接電話,你等一下,而沒有掛電話,這種處理的方式就是互斥。而如果老總拿其另一部電話說,我在接電話,等會我打給你,然后掛了電話,這種處理的方式就是同步。兩者相比,互斥明顯占用系統(tǒng)資源(浪費電話費,浪費別人的時間),而同步則是一種更加好的解決問題的思路。


          zhuan:http://blog.csdn.net/Mailbomb/archive/2009/05/27/4220846.aspx

          posted on 2009-06-15 13:25 找個美女做老婆 閱讀(247) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計

          公告

          本blog已經(jīng)搬到新家了, 新家:www.javaly.cn
           http://www.javaly.cn

          常用鏈接

          留言簿(6)

          隨筆檔案

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 威海市| 抚顺县| 大邑县| 娱乐| 漯河市| 西吉县| 盱眙县| 芦山县| 东辽县| 淮阳县| 平武县| 蒙自县| 邵东县| 龙里县| 河池市| 平凉市| 台湾省| 黔南| 定安县| 云安县| 十堰市| 交城县| 贡嘎县| 广饶县| 苏尼特左旗| 新乐市| 正安县| 广灵县| 巧家县| 七台河市| 波密县| 丰台区| 克山县| 湖南省| 曲松县| 柞水县| 麟游县| 达日县| 舒兰市| 大石桥市| 蒙城县|