日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

JavaThread應(yīng)該注意的問題

 shaobin0604@163.com 2007-04-17

Java Thread應(yīng)該注意的問題

(wang hailong)

 

Java的線程編程非常簡單。但有時會看到一些關(guān)于線程的錯誤用法。下面列出一些應(yīng)該注意的問題。

1.同步對象的恒定性

All java objects are references.

對于局部變量和參數(shù)來說,java里面的int, float, double, boolean等基本數(shù)據(jù)類型,都在棧上。這些基本類型是無法同步的;java里面的對象(根對象是Object),全都在堆里,指向?qū)ο蟮?/span>reference在棧上。

java中的同步對象,實際上是對于reference所指的對象地址進行同步。
需要注意的問題是,千萬不要對同步對象重新賦值。舉個例子。
class A implements Runnable{
  Object lock = new Object();

void run(){
  for(...){
    synchronized(lock){
      // do something
      ...
     
lock = new Object();
    }
  }
}

run
函數(shù)里面的這段同步代碼實際上是毫無意義的。因為每一次lock都給重新分配了新的對象的reference,每個線程都在新的reference同步。
大家可能覺得奇怪,怎么會舉這么一個例子。因為我見過這樣的代碼,同步對象在其它的函數(shù)里被重新賦了新值。
這種問題很難查出來。
所以,一般應(yīng)該把同步對象聲明為final.
final Object lock = new Object();

 

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

2.如何放置共享數(shù)據(jù)

實現(xiàn)線程,有兩種方法,一種是繼承Thread類,一種是實現(xiàn)Runnable接口。

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

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

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

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

3.同步的粒度

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

4.線程之間的通知

這里使用“通知”這個詞,而不用“通信”這個詞,是為了避免詞義的擴大化。

線程之間的通知,通過Object對象的wait()notify() notifyAll() 方法實現(xiàn)。

下面用一個例子,來說明其工作原理:

假設(shè)有兩個線程,AB。共同擁有一個同步對象,lock。

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

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

3.線程A從等待隊列里面出來,進入ready隊列,等待調(diào)度。

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

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

 

例子代碼如下:

public class SharedResource implements Runnable{

  Object lock = new Object();

 

  public void run(){

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

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

   

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

      synchronized(lock){ //線程A通過synchronized(lock) 獲得lock同步對象

       try{

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

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

// 線程A停止運行,進入等待隊列。

       }catch(InterruptedException e){

       }

 

        // 線程A重新獲得lock同步對象之后,繼續(xù)運行。

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

         } // end of synchronized(lock)

       }

 

       if( “B”.equals(threadName)){

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

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

 

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

 

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

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

         } // end of synchronized(lock)

 

   boolean hasLock = Thread.holdsLock(lock); // 檢查B是否擁有lock同步對象。

   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();

 

   // 強迫主線程停止運行,以便線程A開始運行。

  try {

     Thread.sleep(500);

    }catch(InterruptedException e){

    }

 

   Thread B = new Thread(resource,”B”);

   B.start();

  }

}

 

5.跨類的同步對象

對于簡單的問題,可以把訪問共享資源的同步代碼都放在一個類里面。

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

如何在不同的類中,共享同步對象。有幾種方法實現(xiàn),

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

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

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

 

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

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

比如,我們有如下代碼。

String A = “atom”;

String B = “atom”;

我們有理由認為,AB指向同一個字符串常量。即,A==B。

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

String C= new String(“atom”);

String D = new String(“atom”);

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

 

有了上述的認識,我們就可以使用字符串常量作為同步對象。

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

本文并不強烈推薦這種用法,只是說明,有這樣一種方法存在。

 

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

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多