Synchronized 鎖定的是對象而非函數或代碼。
每個Object都有一把鎖(Lock),當進行到Synchronized語句或函數的時候,這把鎖就會被當前的線程(thread)拿走,其他的(thread)再去訪問的時候拿不到鎖就被暫停了。
只有當Synchronized的是同一個對象的纔是線程安全的(thread-safe)
class Test{
public Synchronized void method1(){ //鎖住的對象是this 即當前對象
//........
}
public void method2(){ // 鎖住的對象是this 所以和method1是等同的,
//他們之間也是線程安全的。
Synchronized(this){
//................
}
}
public Synchronized static void method3(){ //鎖住的對象是Test.class,注:
//Test.class 在虛擬機中只可能有一個,但是Test長生的對象
//可以有多個,因爲method3和method1,method2鎖住的
//不同,所以他們之間不同步,不是線程安全的。
}
public void method4(){ //跟method3是相同的效果。
Synchronized(Test.class){
//............
}
}
}
這是Synchronized static 函數與Synchronized instance函數之間的區別。
-----------------------------------------
class Test implements Runnable{
private int[] intArray = new int[10]
public synchronized void addToArray(int[] ar){
// modify the intArray
}
public synchronized void subArray(int[] ary){
// modify the intArray
}
public int[] getIntArray(){
return intArray;
}
}
很多人看到上面這個例子馬上可以斷定這個Test一定是同步的。其實不然,因爲他暴露了intArray給外界,所以可以不通過上面兩個方法去修改intArray這個filed,假如intArray是public protected數據那就更不行了。那我要訪問這個intArray怎麼辦,那就只好clone一個intArray了。
public int[] getIntArray(){
return intArray.clone();
}
-------------------------------------------
private byte[] lock = new byte[0];//建議用使用這樣的lock,因爲這個不任何對象都經濟。
創建一個元素個數爲0的array,並不象創建對象那樣需要調用構造函數,所以速度會快些,此外,內含元素的byte arrays往往在jvm中有着比int arrays 更緊湊的表述形式。
---------------------------------------------
long clocktime = 123123.1;
很多人看到上面這個操作的時候肯定會以爲這個一定是安全的,因爲會認爲這是個原子操作。呵呵 錯了!
long 數據一般採用64bit(跨越兩個32bit word)表述。或許某些jvm實現產品將64-bit操作視爲不可分割的(atomic),但現今大部分jvm實現產品都不這樣。而是將它視爲兩個獨立的32-bit操作。所以有可能發生舊值的前32bit以及其新值的後32bit組成。這個返回值是錯誤的。這是因爲,面對64bit數據,jvm必須執行一次以上的讀寫動作。先前討論的“私有專用副本”與“主內存”也是這個問題。想要改正這個問題,有兩個選擇:同步控制對clocktime變量的訪問,或是將它聲明爲volatile。