Java虚拟机优化--线程死锁排查
线程的通信
例题: 使用两个线程打印1-100. 线程1,线程2交替打印
public class ThreadCommunity {
public static void main(String[] args) {
/**
* 这里创建了两个Runnable子对象
*/
Thread t1=new Thread(new Number(),"线程A");
Thread t2=new Thread(new Number(),"线程B");
t1.start();
t2.start();
}
}
class Number implements Runnable{
private int number=1;
@Override
public void run(){
while(true){
synchronized (this){
//这里其实就是Number.this
/**
* 当前案例只有两个线程,所以用notify()如果好多线程就用notifyAll()
* 线程1进来,notify一下没啥用,因为都不是阻塞的,打印数字wait后阻塞了
* 线程2进来,notify把线程1唤醒了,但是由于当前线程2还没有释放锁,所以线程1得等着,线程2打印数字后wait了
* 线程1现在是就绪状态的,线程2--wait线程1又拿到锁进来了,然后把线程2唤醒了,线程2没抢到锁,还是就绪状态。
*/
notify();
if(number>100){
break;
}
System.out.println(Thread.currentThread().getName()+"打印"+number);
number++;
try {
//注意这里不能使用sleep,在锁住的代码里面写sleep()当前线程不会释放锁
//使用调用如下wait()方法的线程进入阻塞状态,并释放锁。
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
上面这段代码执行后,会出现死锁,排查的方法如下:
使用jps+jstack
-
首先在Windows命令行窗口,输入jps -l 【参数-l表示输出应用程序main class的完整package名】
-
然后输入jatack -l 240736 【打印线程栈】
可以看到线程A和线程B都是先加了锁,然后wait()释放了锁,两个线程都进入阻塞状态,程序死循环。
线程信息块介绍
- 线程名称:“线程B”
- 优先级: prio=5
- jvm线程id:tid=0x000000001e956000 【jvm内部线程的唯一标识】
- 对应系统线程id:nid=0x3b750
- 线程状态:in Object.wait()
- 起始栈地址: [0x000000001f60f000]
- Java thread statck trace:是上面2-7行的信息,Java stack trace提供了大部分信息来精确定位问题根源。
堆栈信息应该逆向解读:程序先执行的是第7行,然后是第6行,依次类推。
- locked <0x000000076b902760> (a thread.Number)
- -waiting on <0x000000076b902760> (a thread.Number)
也就是说对象先上锁,锁住对象0x000000076b902760,然后释放该对象锁,进入waiting状态。
修改代码
我们使用同一个Number对象,则结果正确输出。