Java Thread應該注意的問題 (轉)
Thread應該注意的問題
(wang hailong)
:namespace prefix = o ns = "urn:schemas--com::office" />
Java的執行緒非常簡單。但有時會看到一些關於執行緒的錯誤用法。下面列出一些應該注意的問題。
1.同步的恆定性
All java s are references.
對於區域性變數和引數來說,java裡面的int, float, double, boolean等基本資料型別,都在棧上。這些基本型別是無法同步的;java裡面的物件(根物件是Object),全都在堆裡,指向物件的reference在棧上。
java中的同步物件,實際上是對於reference所指的“物件地址”進行同步。
需要注意的問題是,千萬不要對同步物件重新賦值。舉個例子。
class A implements Runnable{
Object lock = new Object();
void run(){
for(...){
synchronized(lock){
// do something
...
lock = new Object();
}
}
}
run裡面的這段同步程式碼實際上是毫無意義的。因為每一次lock都給重新分配了新的物件的reference,每個執行緒都在新的reference同步。
大家可能覺得奇怪,怎麼會舉這麼一個例子。因為我見過這樣的程式碼,同步物件在其它的函式里被重新賦了新值。
這種問題很難查出來。
所以,一般應該把同步物件宣告為final.
final Object lock = new Object();
使用Singleton Pattern 設計來獲取同步物件,也是一種很好的選擇。
2.如何放置共享資料
實現執行緒,有兩種方法,一種是繼承Thread類,一種是實現Runnable介面。
上面舉的例子,採用實現Runnable介面的方法。本文推薦這種方法。
首先,把需要共享的資料放在一個實現Runnable介面的類裡面,然後,把這個類的例項傳給多個Thread的構造方法。這樣,新建立的多個Thread,都共同擁有一個Runnable例項,共享同一份資料。
如果採用繼承Thread類的方法,就只好使用static靜態成員了。如果共享的資料比較多,就需要大量的static靜態成員,令資料結構混亂,難以擴充套件。這種情況應該儘量避免。
編寫一段多執行緒程式碼,處理一個稍微複雜點的問題。兩種方法的優劣,一試便知。
3.同步的粒度
執行緒同步的粒度越小越好,即,執行緒同步的程式碼塊越小越好。儘量避免用synchronized修飾符來宣告方法。儘量使用synchronized(anObject)的方式,如果不想引入新的同步物件,使用synchronized(this)的方式。而且,synchronized程式碼塊越小越好。
4.執行緒之間的通知
這裡使用“通知”這個詞,而不用“通訊”這個詞,是為了避免詞義的擴大化。
執行緒之間的通知,透過Object物件的wait()和notify() 或notifyAll() 方法實現。
下面用一個例子,來說明其工作原理:
假設有兩個執行緒,A和B。共同擁有一個同步物件,lock。
1.首先,執行緒A透過synchronized(lock) 獲得lock同步物件,然後lock.wait()函式,放棄lock同步物件,執行緒A停止執行,進入等待佇列。
2.執行緒B透過synchronized(lock) 獲得執行緒A放棄的lock同步物件,做完一定的處理,然後呼叫 lock.notify() 或者lock.notifyAll() 通知等待佇列裡面的執行緒A。
3.執行緒A從等待佇列裡面出來,進入ready佇列,等待排程。
4.執行緒B繼續處理,出了synchronized(lock)塊之後,放棄lock同步物件。
5.執行緒A獲得lock同步物件,繼續執行。
例子程式碼如下:
public class SharedRe implements Runnable{
Object lock = new Object();
public void run(){
// 獲取當前執行緒的名稱。
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(); // 呼叫lock.wait()函式,放棄lock同步物件,
// 執行緒A停止執行,進入等待佇列。
}catch(InterruptedException e){
}
// 執行緒A重新獲得lock同步物件之後,繼續執行。
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佇列,等待排程。
//執行緒B繼續處理,出了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.跨類的同步物件
對於簡單的問題,可以把訪問共享資源的同步程式碼都放在一個類裡面。
但是對於複雜的問題,我們需要把問題分為幾個部分來處理,需要幾個不同的類來處理問題。這時,就需要在不同的類中,共享同步物件。比如,在生產者和消費者之間共享同步物件,在讀者和寫者之間共享同步物件。
如何在不同的類中,共享同步物件。有幾種方法實現,
(1)前面講過的方法,使用static靜態成員,(或者使用Singleton Pattern.)
(2)用引數傳遞的方法,把同步物件傳遞給不同的類。
(3)利用字串常量的“原子性”。
對於第三種方法,這裡做一下解釋。一般來說,程式程式碼中的字串常量經過編譯之後,都具有唯一性,即,中不會存在兩份相同的字串常量。
(通常情況下,C++,C語言程式編譯之後,也具有同樣的特性。)
比如,我們有如下程式碼。
String A = “atom”;
String B = “atom”;
我們有理由認為,A和B指向同一個字串常量。即,A==B。
注意,宣告字串變數的程式碼,不符合上面的規則。
String C= new String(“atom”);
String D = new String(“atom”);
這裡的C和D的宣告是字串變數的宣告,所以,C != D。
有了上述的認識,我們就可以使用字串常量作為同步物件。
比如我們在不同的類中,使用synchronized(“myLock”), “myLock”.wait(),“myLock”.notify(), 這樣的程式碼,就能夠實現不同類之間的執行緒同步。
本文並不強烈推薦這種用法,只是說明,有這樣一種方法存在。
本文推薦第二種方法,(2)用引數傳遞的方法,把同步物件傳遞給不同的類。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992318/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- java泛型應該注意的問題。Java泛型
- 面試官:集合使用時應該注意哪些問題?我:應該注意該注意的問題!面試
- Redux的應該注意的問題Redux
- 應用SqlitePCL應該注意的問題SQLite
- 購買硬碟應該注意的幾個問題硬碟
- java高階用法之:JNA型別對映應該注意的問題Java型別
- 應聘高階前端開發,應該注意哪些問題?前端
- Java泛型應該注意的細節Java泛型
- 每個CIO都應該問的IT轉型問題
- UI設計中色彩運用應該注意哪些問題UI
- 雲伺服器的時候應該要注意哪些問題呢?伺服器
- 開發網校原始碼時應該注意的幾個問題原始碼
- Masm中呼叫ZwOpenSection時應該注意的問題 (1千字)ASM
- 軟交換組網應注意的問題與分析(轉)
- 程式設計中對於檔案路徑應該注意的問題程式設計
- Promise使用時應注意的問題Promise
- 使用MFC的Windows 套接字應注意的幾個問題 (轉)Windows
- 高質量移動網站應該注意這6個問題網站
- Oracle 10g資料庫預設安裝應該注意的問題Oracle 10g資料庫
- 軟交換技術組網應注意的問題與分析(轉)
- APP打包上線應注意的問題!APP
- Java面試題:SimpleDateFormat是執行緒安全的嗎?使用時應該注意什麼?Java面試題ORM執行緒
- Python新手入門應該注意的一些問題以及學習方向Python
- Java Thread in JVM (轉)JavathreadJVM
- 應該要PHP轉Java了。。。PHPJava
- 網站URL設計應注意的問題網站
- Java程式設計師面試時應注意的三個經典問題!Java程式設計師面試
- gRPC之應該注意的地方RPC
- 面試應該問公司什麼問題面試
- 面試的時候應該想的問題面試
- 參加Java培訓應該注意什麼事項Java
- WEB開發者應該反問自己的10個問題Web
- java 讀取.txt檔案時,注意的問題Java
- 線上教育程式開發時應注意的問題
- Java春招面試複習:有關於Java Map,應該掌握的8個問題Java面試
- 關於CORS 應該注意的幾點CORS
- 最應該注意的Oracle版本之一Oracle
- 學習Java知識應該注意哪些基礎原則Java