<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    潛魚在淵

    Concentrating on Architectures.

    posts - 77, comments - 309, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    Java線程安全精解

    Posted on 2005-11-17 01:59 非魚 閱讀(23405) 評論(37)  編輯  收藏 所屬分類: Java技術

    一直不敢寫點什么,是因為戰戰兢兢,生怕寫的不好甚至寫錯了會誤人子弟。隨筆可以隨便寫一下,不用太過計較,可是技術從來都要不得半點馬虎,差之毫厘,謬以千里啊!但敝帚自珍又不是我的風格,雖然文筆不好,也要勉為其難了。廢話少說,進入正題。

     

           從我開始接觸Java的多線程起就總是覺得書上講的不是那么清楚。不是說讀完了不會寫,而是對寫出來的多線程代碼懵懵懂懂,不知道每一句會有什么影響,心里感覺忐忑。后來仔細研讀Java語言規范后,才慢慢搞明白一些細節。我主要想說的,也就是這些經驗吧。

     

           首先要搞清楚的是線程的共享資源,共享資源是多線程中每個線程都要訪問的類變量或實例變量,共享資源可以是單個類變量或實例變量,也可以是一組類變量或實例變量。多線程程序可以有多個共享資源。下面描述他們之間的一對多關系(*表示多):

          

                         多線程程序(1----共享資源(*----類變量或實例變量(1…*

     

    只有類變量和實例變量可以成為共享資源,細分如下:

    1.       實現線程的類(繼承Thread類、實現Runnable接口的類)的類變量、實例變量。

    2.       實現線程的類的類變量、實例變量的類變量、實例變量,可以不規范的寫為:TreadClass.ClassOrInstanceVar[.ClassOrInstanceVar]*[]*的內容表示無限可重復。

    3.       不是實現線程的類,但其對象可能是線程的類變量或實例變量。如ServletEJB。這些類的類變量和實例變量,不規范的寫為:ServletOrEJB.ClassOrInstanceVar[.ClassOrInstanceVar]*

    4.       特別注意:局部變量、做為參數傳遞的非類變量、非實例變量不是共享資源。

     

    那么什么是線程安全呢?關于這個問題我在網上百度了一下(沒辦法,有時候GOOGLE用不了),發現不少人在問這個問題,也有不少錯誤的理解。所以我給出一個較容易理解的解釋:在線程中使用共享資源時,能夠保證共享資源在任何時候都是原子的、一致的,這樣的線程就是線程安全的線程。還不太理解?沒有關系,慢慢解釋。

     

    首先來介紹一下共享資源的類型(這是我自己分類的,為了后文好解釋),共享資源從其類型可以分為三類(下文講到變量一律指類變量或實例變量,不再特別指出):

    1.       獨立的基本類型共享資源,如一個簡單的int變量,例:

    public class Cls1 {

           private int a;

           public int getA(){return a;}

           public void setA(int a){this.a = a;}

    }

    可以看到a沒有任何依賴。

    public class Cls2{

           private int a;

           private int b;

           private int c;

           // 沒有對a的訪問方法,aCls外不可見。

    }

    假設上面類中bc都不依賴a,則a是這種類型。

     

    2.       相互依賴的基本類型共享資源,一個類中的幾個基本類型變量互相依賴,但從對象設計的角度又不能單獨把這幾個變量設計成一個類。

    假設上例Cls2中的bc互相依賴,則屬此種情況。

    3.       64位的基本類型變量。這個比較特殊,因為某些機器上64變量會分成兩個32位的操作,所以和1不一樣。如doublelong類型。

    4.       類類型的共享資源。如下例中的obj

    public class Cls3{

           private SomeObj obj;

    }

    public class SomeObj{

           private int a;

           private int b;

    }

     

           其次來看看什么是原子性、一致性。其實在這里我借用了事務ACID屬性的AC,熟悉的朋友就不用我廢話了。所謂原子性,是指一個共享資源的所有屬性在任何時刻都是一起變化、密不可分的;所謂一致性,是指一個共享資源的所有屬性在變化之后一定會達到一個一致的狀態。

     

           最后根據上述四種共享資源類型,來看看如何做到線程安全。

     

    1.       不用做什么,只一個獨立的變量,任何時候它都是原子、一致的。

    2.       使用synchronized關鍵字,保證幾個變量被一起修改、一起讀取。

    3.       使用volatile關鍵字,然后就和1一樣了。

    4.       2一樣處理。

     

    當對訪問共享資源的方法不同時使用synchronized關鍵字時,是什么樣一種情況呢?這是需要特別注意的,這樣不能保證線程安全!看看下面例子的運行結果就知道了(自己運行啊,我不貼結果了):

    /**

     * $Author: $

     * $Date: $

     * $Revision: $

     * $History: $

     *

     * Created by feelyou, at time 22:31:53, 2005-11-16.

     */

     

    public class TestThread extends Thread {

     

      private int a = 0;

      private int b = 0;

     

      public static void main(String[] args) {

        TestThread test = new TestThread();

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

          Thread thread = new Thread(test, "thread-" + i);

          thread.start();

        }

      }

     

      public synchronized void doWrite() {

        a++;

        try {

          sleep((int)(Math.random()*100));

        }

        catch (InterruptedException e) {

        }

        b++;

        try {

          sleep((int)(Math.random()*100));

        }

        catch (InterruptedException e) {

        }

      }

     

      public void print() {

        System.out.println("" + Thread.currentThread().getName() + ":a:" + a);

        System.out.println("" + Thread.currentThread().getName() + ":b:" + b);

      }

     

      public void run() {

        super.run();    //To change body of overridden methods use File | Settings | File Templates.

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

          doWrite();

          print();

        }

      }

     

      public synchronized void start() {

        super.start();    //To change body of overridden methods use File | Settings | File Templates.

      }

    }

     

    ThreadLocalThreadLocal對于線程安全還是很有用的,如果資源不是共享的,那么應該使用ThreadLocal,但如果確實需要在線程間共享資源,ThreadLocal就沒有用了!

     

    最后,來一個完整的線程安全的例子:

    /**

     * $Author: $

     * $Date: $

     * $Revision: $

     * $History: $

     *

     * Created by feelyou, at time 22:31:53, 2005-11-16.

     */

     

    public class TestThread extends Thread {

     

      private int a = 0; //獨立的共享資源

      private int b = 0; //bc互相依賴

      private int c = 0;

      private volatile long d = 0L; //64

    //  private SomeObj obj = new SomeObj(); //對象類型,大家自己寫吧,我就不寫了。

     

      public static void main(String[] args) {

        TestThread test = new TestThread();

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

          Thread thread = new Thread(test, "thread-" + i);

          thread.start();

        }

      }

     

      public synchronized void doWrite() {

        b++;

        try {

          sleep((int)(Math.random()*100));

        }

        catch (InterruptedException e) {

        }

        c++;

        try {

          sleep((int)(Math.random()*100));

        }

        catch (InterruptedException e) {

        }

      }

     

      public synchronized void print() {

        System.out.println("" + Thread.currentThread().getName() + ":b:" + b);

        System.out.println("" + Thread.currentThread().getName() + ":c:" + c);

      }

     

      private void setA(int a) {

          this.a = a;

      }

     

      private int getA() {

          return a;

      }

     

      public long getD() {

          return d;

      }

     

      public void setD(long d) {

          this.d = d;

      }

     

      public void run() {

        super.run();    //To change body of overridden methods use File | Settings | File Templates.

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

          doWrite();

          print();

          setA(i);

          System.out.println(getA());

          setD(18456187413L * i);

          System.out.println(getD());

        }

      }

     

      public synchronized void start() {

        super.start();    //To change body of overridden methods use File | Settings | File Templates.

      }

    }

    寫的比較匆忙,如果有錯誤,還請大家指正,謝謝!

    評論

    # re: Java線程安全詳解  回復  更多評論   

    2005-12-16 10:44 by www
    什么詳解啊 胡說八到,

    # re: Java線程安全詳解  回復  更多評論   

    2005-12-16 12:20 by 非魚
    寫的比較匆忙,如果有錯誤,還請大家指正。

    可能還不夠詳細吧,但是哪里胡說八道了?拜托,如果有錯誤請明確指出好不好?

    # re: Java線程安全精解  回復  更多評論   

    2006-01-19 00:36 by aaa
    還可以呀。呵呵。有用

    # re: Java線程安全精解  回復  更多評論   

    2006-01-24 15:53 by 路過..
    還不錯.是我見過少數描述線程安全沒有什么錯誤理解的文章..
    使用同步比較容易造成性能影響甚至死瑣情況,
    簡單的辦法就是共享的類不使用實例變量,都改用局部變量.
    當一個單例的類有實例變量時就要小心他很可能不是線程安全的了.

    # re: Java線程安全精解  回復  更多評論   

    2006-02-27 20:54 by mrwjx
    沒看懂,關于 “共享資源的類型” 這一段,能用更通俗的話講一下嗎,我看了好幾遍,還是不明白您的意思,另外關于線程安全簡單的講,是不是可以理解成多個線程調用線程安全的類的方法的話,不會出現不可預知的不正常的情況

    # re: Java線程安全精解  回復  更多評論   

    2006-04-10 19:20 by java愛好者
    既然你害怕寫東西,就不要寫了吧,你的思維思路是沒錯的,但是你的表達很不清晰,很容易給初學者造成迷茫困惑,呵呵,以后多寫點代碼吧

    # re: Java線程安全精解  回復  更多評論   

    2006-07-11 15:15 by mn
    樓上的真是sb

    # re: Java線程安全精解  回復  更多評論   

    2006-08-13 09:03 by loocky
    寫的其實不錯

    # re: Java線程安全精解  回復  更多評論   

    2006-08-27 23:12 by happy
    人家好心好意寫出來與大家分享,大家應該支持一下啊!
    有問題認真討論,出口傷人就太不應該了。

    # re: Java線程安全精解  回復  更多評論   

    2006-12-10 10:35 by lyl
    不錯,謝謝,辛苦了

    # re: Java線程安全精解  回復  更多評論   

    2007-03-31 23:32 by xzw
    寫的真的很不錯的,謝謝了

    # re: Java線程安全精解  回復  更多評論   

    2007-06-24 22:46 by 肖建
    你雞兒屎的,寫的還不錯,
    你對JAVA的線程安全已經理解的很不錯了,
    我很佩服你哈,
    你是不是搞了很久這方面的東西了啊,
    不然是沒有這么深厚的功底的哈

    # re: Java線程安全精解  回復  更多評論   

    2007-06-28 16:25 by sinkos
    我很欣賞這位作者:一直不敢寫點什么,是因為戰戰兢兢,生怕寫的不好甚至寫錯了會誤人子弟。隨筆可以隨便寫一下,不用太過計較,可是技術從來都要不得半點馬虎,差之毫厘,謬以千里啊!
    這些話體現作者一絲不茍的態度,科學的態度.
    謝謝你,謝謝這位作者!

    # re: Java線程安全精解  回復  更多評論   

    2007-07-07 09:53 by itkui
    謝謝樓主分享。

    我感覺罵人是不對的,尤其沒有仔細看完就匆匆罵人的同志。

    哪里好哪里不好,你說出來嗎。

    # re: Java線程安全精解  回復  更多評論   

    2007-09-17 16:14 by 同聲傳譯
    愿意將自己的勞動成果與別人分享其實就已經很不錯了。

    # re: Java線程安全精解  回復  更多評論   

    2007-09-28 11:04 by 大王
    不錯。支持。

    # re: Java線程安全精解  回復  更多評論   

    2008-05-18 13:54 by 極地冰冷
    寫的不錯,

    # re: Java線程安全精解  回復  更多評論   

    2008-08-26 09:09 by yp
    不錯

    # re: Java線程安全精解  回復  更多評論   

    2008-08-26 18:25 by 陳堯
    -,還不錯啦.雖然沒看得很懂但是我覺得還是滿仔細的.
    那些講粗話的同志是不是要思考一下自己的素質呢?..

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2008-09-19 08:50 by caoer
    純支持,繼續努力,共勉……

    # re: Java線程安全精解  回復  更多評論   

    2008-10-24 18:10 by xxyw
    看的不是很明白..不過謝謝樓主了..

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2008-11-17 17:23 by XXX
    還不錯

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2008-11-25 01:12 by fbysss
    ---實現線程的類(繼承Thread類、實現Throwable接口的類)
    是不是寫錯了?應該是Runnable接口吧?

    另外<!--[if !supportLists]--> 這些是啥?模板標簽沒替換呢?重新排一下版吧,看上去比較費勁

    # re: Java線程安全精解  回復  更多評論   

    2009-01-13 18:30 by 讀鐵人
    你的第一個例子的那個結果確實是不同步的,不過你知道為什么嗎?寫數據和輸出數據的方法不是同步的,盡管你加了synchronized,但是run方法里面沒有加吧? public synchronized void f()
    {
    for (int i = 0; i < 10; i++) {

    doWrite();

    print();

    }
    }
    然后在run方法里面調用f()就行了。這就是原子性。
    共享資源簡單點講就是線程要共用的變量,一般就是類變量,那像你說的怎么復雜。線程安全就是要保證原子性,你什么時候看你start方法前面要加synchronized的?

    # re: Java線程安全精解  回復  更多評論   

    2009-01-13 18:34 by 讀鐵人
    如果你覺得我說的不對,可以來csdn找我,cy729215495,我也是絕不輕易發貼,誤人子弟。我在blogjava沒有注冊,729215495也是我的qq號碼,可以交流。

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2009-02-11 16:02 by jade
    不錯

    # re: Java線程安全精解  回復  更多評論   

    2009-03-16 20:02 by ee
    e

    # re: Java線程安全精解  回復  更多評論   

    2009-04-17 16:42 by weii
    還是要支持一下樓主啊!幸苦了,罵人的能寫出來嗎?

    # re: Java線程安全精解  回復  更多評論   

    2009-07-12 11:15 by 絕對路過
    Google"線程安全 JAVA"一下,這是第一篇。

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2009-09-07 17:30 by qq
    我想說的是這個繼承了Thread類的線程不會出現問題嗎

    # re: Java線程安全精解  回復  更多評論   

    2009-09-08 10:38 by 亂碼(luan.ma(a)163.com)
    來批批“4. 特別注意:局部變量、做為參數傳遞的非類變量、非實例變量不是共享資源。”

    void f(Object key)
    {
    Person person;
    if (map.contains(key)) {
    person = map.get(key);
    }
    else {
    person = new Person();
    map.put(key, person);
    }

    synchronized (person) {
    ...
    //對 person 的一些操作
    ...
    }
    }

    LZ請看以上代碼

    # re: Java線程安全精解  回復  更多評論   

    2009-11-06 10:46 by remox
    共享資源什么的 要考慮java的內存模型吧

    理論上所有資源都可以成為共享資源,實際情況也的確如此

    但有些資源是無法不成為共享資源,是必然的共享資源,比如靜態變量

    這里就涉及到線程工作棧和主存的同步,

    那對于每個線程自身的存儲區域又是如何分配的呢

    究竟Java的內存模型在多線程環境中是如何進行對資源的管理的。

    你說的這些距離精解還是不夠的啊

    # re: Java線程安全精解  回復  更多評論   

    2009-11-25 10:09 by ddy
    看的出來lz很用心,先謝謝lz了。
    也許是因為lz一直都“戰戰兢兢”,少有這么公開表達觀點,乃至表達不清。就這篇文章,我作為初學者看起來很吃力,在表達方式、條理性上lz還可以有改進。
    再次謝謝lz

    # re: Java線程安全精解  回復  更多評論   

    2009-12-11 11:32 by 路過
    你的第一個例子的那個結果確實是不同步的,不過你知道為什么嗎?寫數據和輸出數據的方法不是同步的,盡管你加了synchronized,但是run方法里面沒有加吧? public synchronized void f()
    {
    for (int i = 0; i < 10; i++) {

    doWrite();

    print();

    }
    }
    然后在run方法里面調用f()就行了。這就是原子性。
    共享資源簡單點講就是線程要共用的變量,一般就是類變量,那像你說的怎么復雜。線程安全就是要保證原子性,你什么時候看你start方法前面要加synchronized的?
    說得太好了

    # re: Java線程安全精解  回復  更多評論   

    2010-08-27 12:27 by 7510
    你這個也敢叫精解?!
    不要誤人子弟了
    趕緊把文章刪了
    還列google第一條呢

    # re: Java線程安全精解[未登錄]  回復  更多評論   

    2011-02-12 10:57 by jeff
    寫得很不錯,比一般人寫得好多 了.樓主是真會的人.頂

    # re: Java線程安全精解  回復  更多評論   

    2011-04-06 15:54 by su30mmkx
    文中
    “1. 不用做什么,只一個獨立的變量,任何時候它都是原子、一致的。”
    反省一下是不是有問題。 本人覺得這種情況下也不是線程安全的。
    主站蜘蛛池模板: 99久久国产免费-99久久国产免费| 91在线免费视频| 亚洲国产精品尤物yw在线| 一级毛片免费播放视频| 亚洲国产精品不卡在线电影| 中文字幕av无码无卡免费| 免费无码AV一区二区| 91天堂素人精品系列全集亚洲| 日韩在线天堂免费观看| 久久久久女教师免费一区| 亚洲成人一级电影| 亚洲高清偷拍一区二区三区 | 亚洲av中文无码字幕色不卡| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 国产亚洲综合网曝门系列| 成人毛片免费在线观看| a毛片久久免费观看| 亚洲日韩国产一区二区三区在线| 亚洲中文字幕在线观看| 女人18毛片a级毛片免费| 日本人成在线视频免费播放| 色吊丝免费观看网站| 亚洲国色天香视频| 亚洲成色在线综合网站| 国产精品高清全国免费观看| 91精品啪在线观看国产线免费| 一级毛片aaaaaa视频免费看 | 精品国产污污免费网站aⅴ| 一区二区三区在线免费| 亚洲欧美日韩国产精品一区| 久久久婷婷五月亚洲97号色| 国产亚洲av片在线观看18女人| 在线观看免费宅男视频| **毛片免费观看久久精品| 久久国产一片免费观看| 国产精品久久久久久亚洲小说| 亚洲avav天堂av在线网爱情| 亚洲国产精品第一区二区| 精品国产日韩亚洲一区| 免费乱理伦在线播放| 日韩免费a级在线观看|