11.9.jstack使用详解及定位死锁问题
有些时候我们需要查看下jvm中的线程执行情况,比如,发现服务器的CPU的负载突然增 高了、出现了死锁、死循环等,我们该如何分析呢?
由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么问题,所以就需要 看下jvm的内部线程的执行情况,然后再进行分析查找出原因。
这个时候,就需要借助于jstack命令了,jstack的作用是将正在运行的jvm的线程情况进 行快照,并且打印出来 :
#用法:jstack <pid>
[root@myshop02 ~]# jstack 3694
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.202-b08 mixed mode):
"Attach Listener" #32 daemon prio=9 os_prio=0 tid=0x00007fed14008800 nid=0x138c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Catalina-utility-2" #31 prio=1 os_prio=0 tid=0x00007fed2005c000 nid=0xe91 waiting on condition [0x00007fed0cfc9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c030dbc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
......
1、线程的状态

由上图可知,在Java中线程的状态一共被分成6种:
1)、初始态(NEW)
创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
2)、运行态(RUNNABLE)
在Java中,运行态包括 就绪态和运行态。
就绪态:
该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运 行。
所有就绪态的线程存放在就绪队列中。
运行态:
获得CPU执行权,正在执行的线程。
由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条 运行态的线程。
3)、阻塞态(BLOCKED)
当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。
而在Java中,阻塞态专指请求锁失败时进入的状态。
由一个阻塞队列存放所有阻塞态的线程。
处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行。
4)、等待态(WAITING)
当前线程中调用wait、join、park函数时,当前线程就会进入等待态。
也有一个等待队列存放所有等待态的线程。
线程处于等待态表示它需要等待其他线程的指示才能继续运行。
进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
5)、超时等待态(TIMED_WAITING)
当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就 会进入该状态;
它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其 他线程唤醒;
进入该状态后释放CPU执行权 和 占有的资源。
与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
6)、终止态(TERMINATED)
线程执行结束后的状态。
2、实战:死锁问题
如果在生产环境发生了死锁,我们将看到的是部署的程序没有任何反应了,这个时候我们可以借助jstack进行分析,下面我们实战下查找死锁的原因。
构造死锁
思路:两个线程thread1,thread2,thread1拥有obj1锁,thread2拥有obj2锁。此时thread1想要获取obj2锁,thread2也想要获取obj1锁,于是出现死锁。
代码如下:
public class TestDeadLock { private static Object obj1 = new Object(); private static Object obj2 = new Object(); public static void main(String[] args) { new Thread(new Thread1()).start(); new Thread(new Thread2()).start(); } private static class Thread1 implements Runnable { @Override public void run() { synchronized (obj1) { try { System.out.println(">>>>>>>>>>>" + Thread.currentThread().getName() + "获取了obj1锁"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println(">>>>>>>>>>>" + Thread.currentThread().getName() + "获取了obj2锁"); } } } } private static class Thread2 implements Runnable { @Override public void run() { synchronized (obj2) { try { System.out.println(">>>>>>>>>>>" + Thread.currentThread().getName() + "获取了obj2锁"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1) { System.out.println(">>>>>>>>>>>" + Thread.currentThread().getName() + "获取了obj1锁"); } } } } }
编码测试
E:\dev_env\jdk8\bin\java "-javaagent:E:\smart_an1\devtool\JetBrains\IntelliJ IDEA 2018.1\lib\idea_rt.jar=20149:E:\smart_an1\devtool\JetBrains\IntelliJ IDEA 2018.1\bin" -Dfile.encoding=UTF-8 -classpath E:\dev_env\jdk8\jre\lib\charsets.jar;E:\dev_env\jdk8\jre\lib\deploy.jar;E:\dev_env\jdk8\jre\lib\ext\access-bridge-64.jar;E:\dev_env\jdk8\jre\lib\ext\cldrdata.jar;E:\dev_env\jdk8\jre\lib\ext\dnsns.jar;E:\dev_env\jdk8\jre\lib\ext\jaccess.jar;E:\dev_env\jdk8\jre\lib\ext\jfxrt.jar;E:\dev_env\jdk8\jre\lib\ext\localedata.jar;E:\dev_env\jdk8\jre\lib\ext\nashorn.jar;E:\dev_env\jdk8\jre\lib\ext\sunec.jar;E:\dev_env\jdk8\jre\lib\ext\sunjce_provider.jar;E:\dev_env\jdk8\jre\lib\ext\sunmscapi.jar;E:\dev_env\jdk8\jre\lib\ext\sunpkcs11.jar;E:\dev_env\jdk8\jre\lib\ext\zipfs.jar;E:\dev_env\jdk8\jre\lib\javaws.jar;E:\dev_env\jdk8\jre\lib\jce.jar;E:\dev_env\jdk8\jre\lib\jfr.jar;E:\dev_env\jdk8\jre\lib\jfxswt.jar;E:\dev_env\jdk8\jre\lib\jsse.jar;E:\dev_env\jdk8\jre\lib\management-agent.jar;E:\dev_env\jdk8\jre\lib\plugin.jar;E:\dev_env\jdk8\jre\lib\resources.jar;E:\dev_env\jdk8\jre\lib\rt.jar;E:\dev_env\IdeaProjects\performance-tuning\out\production\oom com.zte.deadlock.TestDeadLock >>>>>>>>>>>Thread-0获取了obj1锁 >>>>>>>>>>>Thread-1获取了obj2锁 //程序卡死在这里,互相等待锁
使用jstack进行分析
使用jstack命令查看死锁结果并分析如下:
在输出的信息中,已经看到,发现了1个死锁,关键看这个
Last updated
Was this helpful?