原因:Synchronized鎖的是對象,也就是identityHashCode所指向的內存地址中的對象實例(根據對象內存地址生成散列值)
下面先看一個demo
static public class MyRunable implements Runnable{
private Integer ncount = 0;
public void run() {
Integer temp = 0;
//System.out.println("thread id:"+Thread.currentThread().getId()+"count"+ncount+" identityHashCode:"+System.identityHashCode(ncount)+" HashCode:"+ncount.hashCode());
while (temp <= 100){
synchronized (ncount){
ncount++;
temp = ncount;
//System.out.println("thread id:"+Thread.currentThread().getId()+"count"+ncount+" identityHashCode:"+System.identityHashCode(ncount)+" HashCode:"+ncount.hashCode());
System.out.println("thread id:"+Thread.currentThread().getId()+"count:"+ncount);
}
try {
Thread.sleep(1000000000);
}
catch (Exception e){
e.printStackTrace();
}
}
}
}
public static void main(String [] args) throws Exception{
Runnable runnable = new MyRunable();
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
結果輸出(如果結果不一致多運行幾次):
發現結果和我們期望不一樣,count的值沒被鎖住。這是爲什麼呢?難道是成員變量的原因?我們將ncount修改成static
static private Integer ncount = 0;
結果依舊一樣,難道是Integer 類型的問題?,那我們嘗試使用類試試
static public class CA{
}
static public class MyRunable implements Runnable{
private CA lock = new CA();
...
//synchronized (ncount)
synchronized (lock){
...
}
}
發現替換成類之後結果就符合預期。那麼synchronized Interger爲什麼會失敗呢?看一下線程調用堆棧
上圖中,每一個線程鎖住的資源其實都並非是同一個,這就可以解釋爲什麼對Integer類型進行加鎖仍然是非線程安全的。其實Synchronized鎖的是對象,也就是identityHashCode所指向的內存地址中的對象實例(根據對象內存地址生成散列值)
既然Synchronized是根據identityHashCode 所指向的內存地址中的對象實例來鎖的,那麼說明Integer的identityHashCode是發生了變化的,而CA是固定的。下面看日誌:
最後總結下,synchronized(Integer)時,當值發生改變時,基本上每次鎖住的都是不同的對象實例,想要保證線程安全,推薦使用AtomicInteger之類會更靠譜。
參考博客:
https://www.iteye.com/blog/gao-xianglong-2396071