下面舉一個發生死鎖的代碼案例,加深對併發安全的理解。
package cn.zhou;
import java.util.concurrent.CountDownLatch;
/**
線程死鎖等待舉例
* 可以通過JConsole命令工具 去查看
**/
public class SynAddDeadLock implementsRunnable{
private int a,b;
CountDownLatch countDownLatch;
publicSynAddDeadLock(int a,int b,CountDownLatch doneSignal){
this.a = a;
this.b = b;
countDownLatch =doneSignal;
}
@Override
public void run(){
synchronized(Integer.valueOf(a)) {
synchronized(Integer.valueOf(b)) {
System.out.println(Thread.currentThread().getName()+"---"+(a+b));
}
}
countDownLatch.countDown();
}
public static voidmain(String[] args) {
final intthreadCount = 200;
booleanisDeadLock = false;
CountDownLatch doneSignal = newCountDownLatch(threadCount);
for (int i = 0;i < threadCount/2; i++) {
newThread(new SynAddDeadLock(1, 2,doneSignal)).start();
newThread(new SynAddDeadLock(2,1,doneSignal)).start();
}
isDeadLock = true;
try {
System.out.println("has deadlock");
doneSignal.await();
//若所有線程都已經結束 說明不存在死鎖
isDeadLock = false;
} catch(InterruptedException e) {
e.printStackTrace();
}
if(!isDeadLock){
System.out.println("no deadlock");
}
System.out.println("end");
}
}
分析:開啓200個線程分別計算1+2以及2+1的值,其實for循環是可以省略的,兩個線程也可能會導致死鎖,不過那樣概率太小,需要嘗試很多次才能看到效果。一般的話,帶for循環的(如上所述)的版本最多運行2~3次就會遇到死鎖,程序無法結束。
造成死鎖的原因是:Integer.valueOf()方法基於減少對象創建次數和節省內存的考慮,【-128,127】之間的數字會被緩存(默認值,實際值取決於java.lang.Integer.IntegerCache.high參數的設置),當valueOf方法傳入參數在這個範圍之內,將之間返回緩存中的對象。即 代碼中調用了200次的Integer.valueOf()方法一共只返回了兩個不同的對象(1和2).假如在某個線程的兩個synchronized塊之間發生了一次線程切換,那就會出現線程A等着被線程B持有的Integer.valueof(1),線程B又等着被線程A持有的Integer.valueOf(2)結果出現都跑不下去的情景。另外,我們可以可以通過Jconsole jviauslvm等工具查看到線程死鎖等待的具體信息。