Java學習

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

           

          多線程問題及處理1

          12.4 多線程問題及處理

                   多線程編程為程序開發(fā)帶來了很多的方便,但是也帶來了一些問題,這些問題是在程序開發(fā)過程中必須進行處理的問題。

                   這些問題的核心是,如果多個線程同時訪問一個資源,例如變量、文件等,時如何保證訪問安全的問題。在多線程編程中,這種會被多個線程同時訪問的資源叫做臨界資源。

                   下面通過一個簡單的示例,演示多個線程訪問臨界資源時產(chǎn)生的問題。在該示例中,啟動了兩個線程類DataThread的對象,該線程每隔200毫秒輸出一次變量n的值,并將n的值減少1。變量n的值存儲在模擬臨界資源的Data類中,該示例的核心是兩個線程類都使用同一個Data類的對象,這樣Data類的這個對象就是一個臨界資源了。示例代碼如下:

                             package syn1;

          /**

           * 模擬臨界資源的類

           */

          public class Data {

                   public int n;

                   public Data(){

                             n = 60;

                   }

          }

          package syn1;

          /**

           * 測試多線程訪問時的問題

           */

          public class TestMulThread1 {

                   public static void main(String[] args) {

                             Data data = new Data();

                             DataThread d1 = new DataThread(data,"線程1");

                             DataThread d2 = new DataThread(data,"線程2");

                   }

          }

          package syn1;

          /**

           * 訪問數(shù)據(jù)的線程

           */

          public class DataThread extends Thread {

                   Data data;

                   String name;

                   public DataThread(Data data,String name){

                             this.data = data;

                             this.name = name;

                             start();

                   }

                    

                   public void run(){

                             try{

                                      for(int i = 0;i < 10;i++){

                                               System.out.println(name + ":" + data.n);

                                               data.n--;

                                               Thread.sleep(200);

                                      }

                             }catch(Exception e){}

                   }

          }

                   在運行時,因為不同情況下該程序的運行結(jié)果會出現(xiàn)不同,該程序的一種執(zhí)行結(jié)果為:

                             線程1:60

          線程2:60

          線程2:58

          線程1:58

          線程2:56

          線程1:56

          線程2:54

          線程1:54

          線程2:52

          線程1:52

          線程2:50

          線程1:50

          線程2:48

          線程1:48

          線程2:47

          線程1:46

          線程2:44

          線程1:44

          線程2:42

          線程1:42

                   從執(zhí)行結(jié)果來看,第一次都輸出60是可以理解的,因為線程在執(zhí)行時首先輸出變量的值,這個時候變量n的值還是初始值60,而后續(xù)的輸出就比較麻煩了,在開始的時候兩個變量保持一致的輸出,而不是依次輸出n的每個值的內(nèi)容,而到將要結(jié)束時,線程2輸出47這個中間數(shù)值。

                   出現(xiàn)這種結(jié)果的原因很簡單:線程1改變了變量n的值以后,還沒有來得及輸出,這個變量n的值就被線程2給改變了,所以在輸出時看的輸出都是跳躍的,偶爾出現(xiàn)了連續(xù)。

                   出現(xiàn)這個問題也比較容易接受,因為最基本的多線程程序,系統(tǒng)只保證線程同時執(zhí)行,至于哪個先執(zhí)行,哪個后執(zhí)行,或者執(zhí)行中會出現(xiàn)一個線程執(zhí)行到一半,就把CPU的執(zhí)行權(quán)交給了另外一個線程,這樣線程的執(zhí)行順序是隨機的,不受控制的。所以會出現(xiàn)上面的結(jié)果。

                   這種結(jié)果在很多實際應用中是不能被接受的,例如銀行的應用,兩個人同時取一個賬戶的存款,一個使用存折、一個使用卡,這樣訪問賬戶的金額就會出現(xiàn)問題。或者是售票系統(tǒng)中,如果也這樣就出現(xiàn)有人買到相同座位的票,而有些座位的票卻未售出。

                   在多線程編程中,這個是一個典型的臨界資源問題,解決這個問題最基本,最簡單的思路就是使用同步關鍵字synchronized。

                   synchronized關鍵字是一個修飾符,可以修飾方法或代碼塊,其的作用就是,對于同一個對象(不是一個類的不同對象), 當多個線程都同時調(diào)用該方法或代碼塊時,必須依次執(zhí)行,也就是說,如果兩個或兩個以上的線程同時執(zhí)行該段代碼時,如果一個線程已經(jīng)開始執(zhí)行該段代碼,則另 外一個線程必須等待這個線程執(zhí)行完這段代碼才能開始執(zhí)行。就和在銀行的柜臺辦理業(yè)務一樣,營業(yè)員就是這個對象,每個顧客就好比線程,當一個顧客開始辦理 時,其它顧客都必須等待,及時這個正在辦理的顧客在辦理過程中接了一個電話 (類比于這個線程釋放了占用CPU的時間,而處于阻塞狀態(tài)),其它線程也只能等待。

                   使用synchronized關鍵字修改以后的上面的代碼為:

                             package syn2;

          /**

           * 模擬臨界資源的類

           */

          public class Data2 {

                   public int n;

                   public Data2(){

                             n = 60;

                   }

                  

                   public synchronized void action(String name){

                             System.out.println(name + ":" + n);

                             n--;

                   }

          }

          package syn2;

          /**

           * 測試多線程訪問時的問題

           */

          public class TestMulThread2 {

                   public static void main(String[] args) {

                             Data2 data = new Data2();

                             Data2Thread d1 = new Data2Thread(data,"線程1");

                             Data2Thread d2 = new Data2Thread(data,"線程2");

                   }

          }

          package syn2;

          /**

           * 訪問數(shù)據(jù)的線程

           */

          public class Data2Thread extends Thread {

                   Data2 data;

                   String name;

                   public Data2Thread(Data2 data,String name){

                             this.data = data;

                             this.name = name;

                             start();

                   }

                    

                   public void run(){

                             try{

                                      for(int i = 0;i < 10;i++){

                                               data.action(name);

                                               Thread.sleep(200);

                                      }

                             }catch(Exception e){}

                   }

          }

                   該示例代碼的執(zhí)行結(jié)果會出現(xiàn)不同,一種執(zhí)行結(jié)果為:

                             線程1:60

          線程2:59

          線程2:58

          線程1:57

          線程2:56

          線程1:55

          線程2:54

          線程1:53

          線程2:52

          線程1:51

          線程2:50

          線程1:49

          線程1:48

          線程2:47

          線程2:46

          線程1:45

          線程2:44

          線程1:43

          線程2:42

          線程1:41

                   在該示例中,將打印變量n的代碼和變量n變化的代碼組成一個專門的方法action,并且使用修飾符synchronized修改該方法,也就是說對于一個Data2的對象,無論多少個線程同時調(diào)用action方法時,只有一個線程完全執(zhí)行完該方法以后,別的線程才能夠執(zhí)行該方法。這就相當于一個線程執(zhí)行到該對象的synchronized方法時,就為這個對象加上了一把鎖,鎖住了這個對象,別的線程在調(diào)用該方法時,發(fā)現(xiàn)了這把鎖以后就繼續(xù)等待下去了。




          (轉(zhuǎn))http://blog.csdn.net/Mailbomb/archive/2009/05/23/4210889.aspx

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


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


          網(wǎng)站導航:
           

          導航

          統(tǒng)計

          公告

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

          常用鏈接

          留言簿(6)

          隨筆檔案

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 花莲县| 东乡族自治县| 桂东县| 拉萨市| 亚东县| 安陆市| 古田县| 台北县| 育儿| 渑池县| 鹤庆县| 西贡区| 高碑店市| 乐平市| 北安市| 多伦县| 崇文区| 浪卡子县| 三台县| 玉山县| 元氏县| 治县。| 故城县| 揭西县| 冀州市| 赤水市| 邢台市| 界首市| 佛坪县| 博客| 徐州市| 会宁县| 黔西| 丰台区| 扬州市| 农安县| 通辽市| 锦州市| 托克托县| 沭阳县| 虎林市|