下面举一个发生死锁的代码案例,加深对并发安全的理解。
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等工具查看到线程死锁等待的具体信息。