简介
目的是介绍Jconsole查看死锁线程的状态
例子
public class ThreadDeadLockTestCase_2 {
/**
* 线程死锁等待演示
*/
static class SynAddRunnalbe implements Runnable {
int a, b;
public SynAddRunnalbe(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
//锁定A
synchronized (Integer.valueOf(b)) {
//锁定B
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunnalbe(1, 2)).start();
new Thread(new SynAddRunnalbe(2, 1)).start();
}
}
}
JDK1.6环境运行,包括jconsole
这段代码开了200个线程去分别计算1+2以及2+1的值,理论上for循环都是可省略的,两个线程也可能会导致死锁,不过那样概率太小,需要尝试运行很多次才能看到死锁的效果。如果运气不是特别差的话,上面带for循环的版本最多运行两三次就会遇到线程死锁,程序无法结束。
造成死锁的根本原因是Integer.valueOf()
方法出于减少对象创建次数和节省内存的考虑,会对数值为-128~127
之间的Integer对象进行缓存
[2] ,如果valueOf()方法传入的参数在这个范围之内,就直接返回缓存中的对象。
《Java虚拟机规范》中明确要求缓存的默认值,实际值可以调整,具体取决于
java.lang.Integer.Integer-Cache.high参数的设置
也就是说代码中尽管调用了200次Integer.valueOf()方法,但一共只返回了两个不同的Integer对象。假如某个线程的两个synchronized块之间发生了一次线程切换,那就会出现线程A在等待被线程B持有的
Integer.valueOf(1),线程B又在等待被线程A持有的Integer.valueOf(2),结果大家都跑不下去的情况。
出现线程死锁之后,点击JConsole线程面板的“检测到死锁
”按钮,将出现一个新的“死锁
”页签,如下图所示。
上图中很清晰地显示,线程Thread-96
在等待一个被线程Thread-67
持有的Integer对象,而点击线程Thread-67则显示它也在等待一个被线程Thread-96持有的Integer对象,这样两个线程就互相卡住,除非牺牲其中一个,否则死锁无法释放。