關(guān)注技術(shù),關(guān)注生活

          任何事情只要開(kāi)始去做,永遠(yuǎn)不會(huì)太遲。
          posts - 5, comments - 23, trackbacks - 0, articles - 18
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          [轉(zhuǎn)]Java 線程應(yīng)該注意的問(wèn)題

          Posted on 2006-12-09 13:45 errorfun 閱讀(207) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JAVA
          1.同步對(duì)象的恒定性All java objects are references.

            對(duì)于局部變量和參數(shù)來(lái)說(shuō),java里面的int, float, double, boolean等基本數(shù)據(jù)類(lèi)型,都在棧上。這些基本類(lèi)型是無(wú)法同步的;java里面的對(duì)象(根對(duì)象是Object),全都在堆里,指向?qū)ο蟮膔eference在棧上。

            java中的同步對(duì)象,實(shí)際上是對(duì)于reference所指的“對(duì)象地址”進(jìn)行同步。

            需要注意的問(wèn)題是,千萬(wàn)不要對(duì)同步對(duì)象重新賦值。舉個(gè)例子。

            

          class ?A? implements ?Runnable {

            Object?lock?
          = ? new ?Object();

            
          void ?run() {

            ???
          for () {

            ??????
          synchronized (lock) {

            ??????
          // ?do?something

            ??????
          //

            ????????lock?
          = ? new ?Object();? 
          ??????????}

          ???????}
          ?  
          ???}
          ?  
          }

          ?  run函數(shù)里面的這段同步代碼實(shí)際上是毫無(wú)意義的。因?yàn)槊恳淮蝜ock都給重新分配了新的對(duì)象的reference,每個(gè)線程都在新的reference同步。

            大家可能覺(jué)得奇怪,怎么會(huì)舉這么一個(gè)例子。因?yàn)槲乙?jiàn)過(guò)這樣的代碼,同步對(duì)象在其它的函數(shù)里被重新賦了新值。

            這種問(wèn)題很難查出來(lái)。

            所以,一般應(yīng)該把同步對(duì)象聲明為final.

            final Object lock = new Object();

            使用Singleton Pattern 設(shè)計(jì)模式來(lái)獲取同步對(duì)象,也是一種很好的選擇。

            2.如何放置共享數(shù)據(jù)實(shí)現(xiàn)線程,有兩種方法,一種是繼承Thread類(lèi),一種是實(shí)現(xiàn)Runnable接口。

            上面舉的例子,采用實(shí)現(xiàn)Runnable接口的方法。本文推薦這種方法。

            首先,把需要共享的數(shù)據(jù)放在一個(gè)實(shí)現(xiàn)Runnable接口的類(lèi)里面,然后,把這個(gè)類(lèi)的實(shí)例傳給多個(gè)Thread的構(gòu)造方法。這樣,新創(chuàng)建的多個(gè)Thread,都共同擁有一個(gè)Runnable實(shí)例,共享同一份數(shù)據(jù)。

            如果采用繼承Thread類(lèi)的方法,就只好使用static靜態(tài)成員了。如果共享的數(shù)據(jù)比較多,就需要大量的static靜態(tài)成員,令程序數(shù)據(jù)結(jié)構(gòu)混亂,難以擴(kuò)展。這種情況應(yīng)該盡量避免。

            編寫(xiě)一段多線程代碼,處理一個(gè)稍微復(fù)雜點(diǎn)的問(wèn)題。兩種方法的優(yōu)劣,一試便知。

            3.同步的粒度線程同步的粒度越小越好,即,線程同步的代碼塊越小越好。盡量避免用synchronized修飾符來(lái)聲明方法。盡量使用synchronized(anObject)的方式,如果不想引入新的同步對(duì)象,使用synchronized(this)的方式。而且,synchronized代碼塊越小越好。

            4.線程之間的通知這里使用“通知”這個(gè)詞,而不用“通信”這個(gè)詞,是為了避免詞義的擴(kuò)大化。

            線程之間的通知,通過(guò)Object對(duì)象的wait()和notify() 或notifyAll() 方法實(shí)現(xiàn)。

            下面用一個(gè)例子,來(lái)說(shuō)明其工作原理:

            假設(shè)有兩個(gè)線程,A和B。共同擁有一個(gè)同步對(duì)象,lock。

            1.首先,線程A通過(guò)synchronized(lock) 獲得lock同步對(duì)象,然后調(diào)用lock.wait()函數(shù),放棄lock同步對(duì)象,線程A停止運(yùn)行,進(jìn)入等待隊(duì)列。

            2.線程B通過(guò)synchronized(lock) 獲得線程A放棄的lock同步對(duì)象,做完一定的處理,然后調(diào)用 lock.notify() 或者lock.notifyAll() 通知等待隊(duì)列里面的線程A。

            3.線程A從等待隊(duì)列里面出來(lái),進(jìn)入ready隊(duì)列,等待調(diào)度。

            4.線程B繼續(xù)處理,出了synchronized(lock)塊之后,放棄lock同步對(duì)象。

            5.線程A獲得lock同步對(duì)象,繼續(xù)運(yùn)行。

            例子代碼如下:

            

          public ? class ?SharedResource? implements ?Runnable {

            Object?lock?
          = ? new ?Object();

            
          public ? void ?run() {

            
          // ?獲取當(dāng)前線程的名稱(chēng)。

            String?threadName?
          = ?Thread.currentThread().getName();

            
          if (?“A”.equals(threadName)) {

            
          synchronized (lock) {? // 線程A通過(guò)synchronized(lock)?獲得lock同步對(duì)象

            
          try {

            System.out.println(“?A?gives?up?lock.”);

            lock.wait();?
          // ?調(diào)用lock.wait()函數(shù),放棄lock同步對(duì)象,

            
          // ?線程A停止運(yùn)行,進(jìn)入等待隊(duì)列。

            }
          catch (InterruptedException?e) {?  } ?   // ?線程A重新獲得lock同步對(duì)象之后,繼續(xù)運(yùn)行。

            System.out.println(“?A?got?lock?again?and?
          continue ?to?run.”);

            }
          ? // ?end?of?synchronized(lock)?  }?  if(?“B”.equals(threadName)){

            
          synchronized (lock) { // 線程B通過(guò)synchronized(lock)?獲得線程A放棄的lock同步對(duì)象

            System.out.println(“B?got?lock.”);

            lock.notify();?
          // 通知等待隊(duì)列里面的線程A,進(jìn)入ready隊(duì)列,等待調(diào)度。

            
          // 線程B繼續(xù)處理,出了synchronized(lock)塊之后,放棄lock同步對(duì)象。

            System.out.println(“B?gives?up?lock.”);

            }
          ? // ?end?of?synchronized(lock)

            
          boolean ?hasLock? = ?Thread.holdsLock(lock);? // ?檢查B是否擁有l(wèi)ock同步對(duì)象。

            System.out.println(“B?has?lock?
          ? ? -- ?”?hasLock);? // ?false.?  }?  }?  }?  public?class?TestMain{

            
          public ? static ? void ?main() {

            Runnable?resource?
          = ? new ?SharedResource();

            Thread?A?
          = ? new ?Thread(resource,”A”);

            A.start();

            
          // ?強(qiáng)迫主線程停止運(yùn)行,以便線程A開(kāi)始運(yùn)行。

            
          try ? {

            Thread.sleep(
          500 );

            }
          catch (InterruptedException?e) {?  } ?  Thread?B? = ? new ?Thread(resource,”B”);

            B.start();?  }
          ?  }
          ?


             5.跨類(lèi)的同步對(duì)象對(duì)于簡(jiǎn)單的問(wèn)題,可以把訪問(wèn)共享資源的同步代碼都放在一個(gè)類(lèi)里面。

            但是對(duì)于復(fù)雜的問(wèn)題,我們需要把問(wèn)題分為幾個(gè)部分來(lái)處理,需要幾個(gè)不同的類(lèi)來(lái)處理問(wèn)題。這時(shí),就需要在不同的類(lèi)中,共享同步對(duì)象。比如,在生產(chǎn)者和消費(fèi)者之間共享同步對(duì)象,在讀者和寫(xiě)者之間共享同步對(duì)象。

            如何在不同的類(lèi)中,共享同步對(duì)象。有幾種方法實(shí)現(xiàn),

            (1)前面講過(guò)的方法,使用static靜態(tài)成員,(或者使用Singleton Pattern.)

            (2)用參數(shù)傳遞的方法,把同步對(duì)象傳遞給不同的類(lèi)。

            (3)利用字符串常量的“原子性”。

            對(duì)于第三種方法,這里做一下解釋。一般來(lái)說(shuō),程序代碼中的字符串常量經(jīng)過(guò)編譯之后,都具有唯一性,即,內(nèi)存中不會(huì)存在兩份相同的字符串常量。

            (通常情況下,C ,C語(yǔ)言程序編譯之后,也具有同樣的特性。)

            比如,我們有如下代碼。

            String A = “atom”;

            String B = “atom”;

            我們有理由認(rèn)為,A和B指向同一個(gè)字符串常量。即,A==B。

            注意,聲明字符串變量的代碼,不符合上面的規(guī)則。

            String C= new String(“atom”);

            String D = new String(“atom”);

            這里的C和D的聲明是字符串變量的聲明,所以,C != D。

            有了上述的認(rèn)識(shí),我們就可以使用字符串常量作為同步對(duì)象。

            比如我們?cè)诓煌念?lèi)中,使用synchronized(“myLock”), “myLock”.wait(),“myLock”.notify(), 這樣的代碼,就能夠?qū)崿F(xiàn)不同類(lèi)之間的線程同步。

            本文并不強(qiáng)烈推薦這種用法,只是說(shuō)明,有這樣一種方法存在。

            本文推薦第二種方法,(2)用參數(shù)傳遞的方法,把同步對(duì)象傳遞給不同的類(lèi)。

          主站蜘蛛池模板: 东兰县| 甘泉县| 尉犁县| 楚雄市| 堆龙德庆县| 长沙市| 兰西县| 额济纳旗| 临澧县| 赤水市| 白银市| 额尔古纳市| 滨海县| 上栗县| 新河县| 民乐县| 遂溪县| 晋江市| 凉城县| 华阴市| 萍乡市| 宽城| 宜丰县| 台中市| 团风县| 会泽县| 鄂伦春自治旗| 历史| 探索| 巴林右旗| 开江县| 新和县| 高碑店市| 惠州市| 四川省| 蓬莱市| 莆田市| 南华县| 汕头市| 柘荣县| 朝阳市|