Dump
Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。Dump文件一般用来给驱动程序编写人员调试驱动程序用的,在java中用来分析正在运行的程序在内存中的堆栈信息。
jstack
Jstack是Jdk自带的线程(栈)跟踪工具,主要用来查看Java线程的调用堆栈,可以用来分析线程问题(如死锁)。可根据指定java进程ID打印指定Java进程的线程堆栈信息。可以定位到线程阻塞、死循环、死锁等源头代码,java程序员必会技能之一。
用法很简单:jstack pid,一般不加参数直接用就行
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
jstack可以针对活着的进程做本地的或远程的线程dump或针对core文件做线程dump。jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
java线程状态
1.NEW,未启动的。不会出现在Dump中。
2.RUNNABLE,在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。
3.BLOCKED,受阻塞并等待监视器锁。被某个锁(synchronizers)給block住了。
4.WATING,无限期等待另一个线程执行特定操作。等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。
5.TIMED_WATING,有时限的等待另一个线程的特定操作。和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。
6.TERMINATED,已退出的。
Monitor
在多线程的 JAVA程序中,实现线程之间的同步,就是通过Monitor实现的。 Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。
进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
拥有者(The Owner):表示某一线程成功竞争到对象锁。
等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。
从图中可以看出,一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”
,而其它线程都是 “Waiting Thread”
,分别在两个队列 “ Entry Set”
和 “Wait Set”
里面等候。在 “Entry Set”
中等待的线程状态是 “Waiting for monitor entry”
,而在“Wait Set”
中等待的线程状态是 “in Object.wait()”
。 先看 “Entry Set”里面的线程。我们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。对应的 code就像:
1 synchronized(obj) {
2 //...
3 }
多线程分析
线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态
thread-1:locked <0x000000076bf62208>
说明线程1对地址为0x000000076bf62208对象进行了加锁;
thread-2:waiting to lock <0x000000076bf62208>
说明线程2在等待地址为0x000000076bf62208对象上的锁;
thread-2:waiting for monitor entry [0x000000001e21f000]
说明线程2是通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor
线程1和2都处于WAITING状态
线程1和2都是先locked <0x000000076bf62500>
,再waiting on <0x000000076bf62500>
,之所以先锁再等同一个对象,是因为wait方法需要先通过synchronized获得该地址对象的monitor;
waiting on <0x000000076bf62500>
说明线程执行了wait方法之后,释放了monitor,进入到"Wait Set"队列,等待其它线程执行地址为0x000000076bf62500对象的notify方法,并唤醒自己
死循环
1 package io.guangsoft.jstack;
2
3 public class EndlessLoop {
4 public static void main(String args[]) {
5 while(true) {
6 System.out.println("endless loop...");
7 }
8 }
9 }
使用jstack查看堆栈信息
我们可以清晰的看到main线程处于RUNNABLE,并能看到是哪个包哪个类下的第几行。
线程等待
1 package io.guangsoft.jstack;
2
3 import java.util.concurrent.ExecutorService;
4 import java.util.concurrent.Executors;
5
6 class TestTask implements Runnable {
7 @Override
8 public void run() {
9 synchronized (this) {
10 try {
11 wait();
12 } catch (InterruptedException e) {
13 e.printStackTrace();
14 }
15 }
16 }
17 }
18
19 public class Wait{
20 public static void main(String[] args) throws Exception {
21 ExecutorService ex = Executors.newFixedThreadPool(1);
22 ex.execute(new TestTask());
23 }
24 }
死锁
1 package io.guangsoft.jstack;
2
3 import java.util.concurrent.ExecutorService;
4 import java.util.concurrent.Executors;
5
6 class Task implements Runnable {
7 private int order;
8 private Object obj1;
9 private Object obj2;
10
11 public Task(int order, Object obj1, Object obj2) {
12 this.order = order;
13 this.obj1 = obj1;
14 this.obj2 = obj2;
15 }
16
17 public void test1() throws InterruptedException {
18 synchronized (obj1) {
19 //建议线程调取器切换到其它线程运行
20 Thread.yield();
21 synchronized (obj2) {
22 System.out.println("test1");
23 }
24
25 }
26 }
27 public void test2() throws InterruptedException {
28 synchronized (obj2) {
29 Thread.yield();
30 synchronized (obj1) {
31 System.out.println("test2");
32 }
33
34 }
35 }
36
37 @Override
38 public void run() {
39 while (true) {
40 try {
41 if(this.order == 1){
42 this.test1();
43 } else {
44 this.test2();
45 }
46 } catch (InterruptedException e) {
47 e.printStackTrace();
48 }
49 }
50 }
51 }
52
53 public class DeadLock {
54 public static void main(String[] args) throws Exception {
55 Object obj1 = new Object();
56 Object obj2 = new Object();
57 ExecutorService ex = Executors.newFixedThreadPool(10);
58 for (int i = 0; i < 10; i++) {
59 int order = i % 2 == 0 ? 1 : 0;
60 ex.execute(new Task(order, obj1, obj2));
61 }
62 }
63 }