<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 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    Java線程安全精解

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

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

     

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

     

           首先要搞清楚的是線程的共享資源,共享資源是多線程中每個(gè)線程都要訪問的類變量或?qū)嵗兞浚蚕碣Y源可以是單個(gè)類變量或?qū)嵗兞浚部梢允且唤M類變量或?qū)嵗兞俊6嗑€程程序可以有多個(gè)共享資源。下面描述他們之間的一對多關(guān)系(*表示多):

          

                         多線程程序(1----共享資源(*----類變量或?qū)嵗兞浚?/span>1…*

     

    只有類變量和實(shí)例變量可以成為共享資源,細(xì)分如下:

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

    2.       實(shí)現(xiàn)線程的類的類變量、實(shí)例變量的類變量、實(shí)例變量,可以不規(guī)范的寫為:TreadClass.ClassOrInstanceVar[.ClassOrInstanceVar]*[]*的內(nèi)容表示無限可重復(fù)。

    3.       不是實(shí)現(xiàn)線程的類,但其對象可能是線程的類變量或?qū)嵗兞俊H?/span>ServletEJB。這些類的類變量和實(shí)例變量,不規(guī)范的寫為:ServletOrEJB.ClassOrInstanceVar[.ClassOrInstanceVar]*

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

     

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

     

    首先來介紹一下共享資源的類型(這是我自己分類的,為了后文好解釋),共享資源從其類型可以分為三類(下文講到變量一律指類變量或?qū)嵗兞浚辉偬貏e指出):

    1.       獨(dú)立的基本類型共享資源,如一個(gè)簡單的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外不可見。

    }

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

     

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

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

    3.       64位的基本類型變量。這個(gè)比較特殊,因?yàn)槟承C(jī)器上64變量會(huì)分成兩個(gè)32位的操作,所以和1不一樣。如doublelong類型。

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

    public class Cls3{

           private SomeObj obj;

    }

    public class SomeObj{

           private int a;

           private int b;

    }

     

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

     

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

     

    1.       不用做什么,只一個(gè)獨(dú)立的變量,任何時(shí)候它都是原子、一致的。

    2.       使用synchronized關(guān)鍵字,保證幾個(gè)變量被一起修改、一起讀取。

    3.       使用volatile關(guān)鍵字,然后就和1一樣了。

    4.       2一樣處理。

     

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

    /**

     * $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對于線程安全還是很有用的,如果資源不是共享的,那么應(yīng)該使用ThreadLocal,但如果確實(shí)需要在線程間共享資源,ThreadLocal就沒有用了!

     

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

    /**

     * $Author: $

     * $Date: $

     * $Revision: $

     * $History: $

     *

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

     */

     

    public class TestThread extends Thread {

     

      private int a = 0; //獨(dú)立的共享資源

      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.

      }

    }

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

    評論

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2006-08-13 09:03 by loocky
    寫的其實(shí)不錯(cuò)

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

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

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

    2006-12-10 10:35 by lyl
    不錯(cuò),謝謝,辛苦了

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

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

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

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

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

    2007-06-28 16:25 by sinkos
    我很欣賞這位作者:一直不敢寫點(diǎn)什么,是因?yàn)閼?zhàn)戰(zhàn)兢兢,生怕寫的不好甚至寫錯(cuò)了會(huì)誤人子弟。隨筆可以隨便寫一下,不用太過計(jì)較,可是技術(shù)從來都要不得半點(diǎn)馬虎,差之毫厘,謬以千里啊!
    這些話體現(xiàn)作者一絲不茍的態(tài)度,科學(xué)的態(tài)度.
    謝謝你,謝謝這位作者!

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

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

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

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

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

    2007-09-17 16:14 by 同聲傳譯
    愿意將自己的勞動(dòng)成果與別人分享其實(shí)就已經(jīng)很不錯(cuò)了。

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

    2007-09-28 11:04 by 大王
    不錯(cuò)。支持。

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

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

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

    2008-08-26 09:09 by yp
    不錯(cuò)

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

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

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

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

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

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

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

    2008-11-17 17:23 by XXX
    還不錯(cuò)

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

    2008-11-25 01:12 by fbysss
    ---實(shí)現(xiàn)線程的類(繼承Thread類、實(shí)現(xiàn)Throwable接口的類)
    是不是寫錯(cuò)了?應(yīng)該是Runnable接口吧?

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

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

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

    doWrite();

    print();

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

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

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

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

    2009-02-11 16:02 by jade
    不錯(cuò)

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

    2009-03-16 20:02 by ee
    e

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

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

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

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

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

    2009-09-07 17:30 by qq
    我想說的是這個(gè)繼承了Thread類的線程不會(huì)出現(xiàn)問題嗎

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

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

    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線程安全精解  回復(fù)  更多評論   

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

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

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

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

    那對于每個(gè)線程自身的存儲(chǔ)區(qū)域又是如何分配的呢

    究竟Java的內(nèi)存模型在多線程環(huán)境中是如何進(jìn)行對資源的管理的。

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

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

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

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

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

    doWrite();

    print();

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

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

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

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

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

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

    2011-04-06 15:54 by su30mmkx
    文中
    “1. 不用做什么,只一個(gè)獨(dú)立的變量,任何時(shí)候它都是原子、一致的。”
    反省一下是不是有問題。 本人覺得這種情況下也不是線程安全的。
    主站蜘蛛池模板: 日韩在线视频免费看| 亚洲一区二区免费视频| 最近中文字幕mv免费高清视频8| 亚洲91av视频| 国产无遮挡色视频免费视频 | 亚洲AV无码乱码在线观看牲色| 中文在线免费看视频| 亚洲Av高清一区二区三区| 亚洲春色在线观看| 亚洲成色www久久网站夜月| 成人性生免费视频| 日韩精品在线免费观看| 国产成人亚洲毛片| 亚洲精品在线免费观看| 亚洲午夜久久久久久久久电影网| 久久笫一福利免费导航| 男人的天堂网免费网站| 日日麻批免费40分钟无码 | 国产精品亚洲A∨天堂不卡| 免费看国产曰批40分钟| 久久受www免费人成_看片中文| av无码国产在线看免费网站| 国产精品极品美女自在线观看免费| 精品国产日韩久久亚洲| 亚洲美女视频一区| 亚洲不卡中文字幕无码| 亚洲综合在线观看视频| 亚洲码国产精品高潮在线| 亚洲国产精品高清久久久| 久久狠狠高潮亚洲精品| 亚洲国产成人精品激情| 久久精品国产亚洲av水果派| 2019亚洲午夜无码天堂| 亚洲av综合日韩| 久久亚洲色WWW成人欧美| 亚洲中文字幕久久精品无码A| 亚洲一级片在线播放| 亚洲熟妇AV乱码在线观看| 日韩成人毛片高清视频免费看| 亚洲AV综合永久无码精品天堂 | 亚洲国产精品一区二区三区久久 |