Jstack是什么?

jstack是java虚拟机自带的一种堆栈跟踪工具。
jstack主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

Jstack分析线程堆栈信息的例子

如程序中发生这样的死锁问题该如何排查呢?我们可以使用java自带的jstack命令进行排查。
1、使用jps、ps -ef | grep java查看当前java进程的pid,严重情况下可以使用top命令查看当前系统cpu/内存使用率最高的进程pid。
这里我们的死锁的pid是:3429,这里程序很简单,虽然程序死锁,没有占用很多资源。

2、使用top -Hp 3429命令查看进程里面占用最多的资源的线程。
这里我们看到的占用最多资源的线程是:3440。
3、使用命令printf “%x\n” 3440 把线程pid转换成16进制数,得到:d70。
4、使用jstack 3429 | grep -20 d70命令查询该线程阻塞的地方。
到这里就基本跟踪完毕,去代码所在行看看为什么死锁吧。

线程状态

想要通过jstack命令来分析线程的情况的话,首先要知道线程都有哪些状态,下面这些状态是我们使用jstack命令查看线程堆栈信息时可能会看到的线程的几种状态:

  1. NEW,未启动的。不会出现在Dump中。
  2. RUNNABLE,在虚拟机内执行的。
  3. BLOCKED,受阻塞并等待监视器锁。
  4. WATING,无限期等待另一个线程执行特定操作。
  5. TIMED_WATING,有时限的等待另一个线程的特定操作。
  6. TERMINATED,已退出的。

NEW

当线程被创建出来还没有被调用start()时候的状态。

2.2 RUNNABLE

当线程被调用了start(),且处于等待操作系统分配资源(如CPU)、等待IO连接、正在运行状态,即表示Running状态和Ready状态。注:不一定被调用了start()立刻会改变状态,还有一些准备工作,这个时候的状态是不确定的。

2.3 BLOCKED

等待监视锁,这个时候线程被操作系统挂起。当进入synchronized块/方法或者在调用wait()被唤醒/超时之后重新进入synchronized块/方法,锁被其它线程占有,这个时候被操作系统挂起,状态为阻塞状态。阻塞状态的线程,即使调用interrupt()方法也不会改变其状态。

2.4 WAITING

无条件等待,当线程调用wait()/join()/LockSupport.park()不加超时时间的方法之后所处的状态,直到另一个线程在这个对象上调用了Object.notify()或Object.notifyAll()方法才能恢复,join()的线程需要特定的线程结束才会执行。如果没有被唤醒或等待的线程没有结束,那么将一直等待,当前状态的线程不会被分配CPU资源和持有锁。

2.5 TIMED_WAITING

有条件的等待,当线程调用sleep(睡眠时间)/wait(等待时间)/join(等待时间)/
LockSupport.parkNanos(等待时间)/LockSupport.parkUntil(等待时间)方法之后所处的状态,在指定的时间没有被唤醒或者等待线程没有结束,会被系统自动唤醒,正常退出。

2.6 TERMINATED

执行完了run()方法。其实这只是Java语言级别的一种状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求,而在Java语言级别只是通过Java代码看到的线程状态而已。

(引自:https://blog.csdn.net/shi2huang/article/details/80289155 )

Monitor

Monitor其实是一种同步工具,也可以说是一种同步机制。每一个Java对象都有成为Monitor的“潜质”。这是为什么?因为在Java的设计中,每一个对象自打娘胎里出来,就带了一把看不见的锁,通常我们叫“内部锁”,或者“Monitor锁”,或者“Intrinsic lock”。下面这个图,描述了线程和 Monitor之间关系,以及线程的状态转换图:

  1. 进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
  2. 拥有者(The Owner):表示某一线程成功竞争到对象锁。
  3. 等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

从图中可以看出,一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。

线程动作

  1. runnable:状态一般为RUNNABLE。
  2. in Object.wait():等待区等待,状态为WAITING或TIMED_WAITING。
  3. waiting for monitor entry:进入区等待,状态为BLOCKED。
  4. waiting on condition:等待区等待、被park。
  5. sleeping:休眠的线程,调用了Thread.sleep()。
runnable

就算是处在runnable也能是在等待,比如当前线程发送http请求还没回来,线程的状态还是runable。

Wait on condition

该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合stacktrace来分析。
最常见的情况就是线程处于sleep状态,等待被唤醒。
常见的情况还有等待网络IO:在java引入nio之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。在NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的CPU时间,相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用dtrace工具看系统调用的情况,如果观察到read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。(来自http://www.blogjava.net/jzone/articles/303979.html)

小结:

  1. 1、线程状态为“waiting for monitor entry”:

意味着它 在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。 此时线程状态一般都是 Blocked:
java.lang.Thread.State: BLOCKED (on object monitor)

  1. 2、线程状态为“waiting on condition”:

说明它在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。 此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。

  1. 3、如果大量线程在“waiting for monitor entry”:

可能是一个全局锁阻塞住了大量线程。 如果短时间内打印的 thread dump 文件反映,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。

  1. 4、如果大量线程在“waiting on condition”:

可能是它们又跑去获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。所以如果你发现有大量的线程都处在 Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。

  1. 线程状态为“in Object.wait()”:

说明它获得了监视器之后,又调用了 java.lang.Object.wait() 方法。
每个Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。当线程获得了 Monitor,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。 此时线程状态大致为以下几种:
java.lang.Thread.State: TIMED_WAITING (on object monitor);
java.lang.Thread.State: WAITING (on object monitor);
一般都是RMI相关线程(RMI、RenewClean、 GC Daemon、RMI Reaper),GC线程(Finalizer),引用对象垃圾回收线程(Reference Handler)等系统线程处于这种状态。

Java方法状态转换

  1. sleep:进入TIMED_WAITING状态,不出让锁
  2. wait:进入TIMED_WAITING状态,出让锁,并进入对象的等待队列
  3. park:进入WAITING状态,对比wait不需要获得锁就可以让线程WAITING,通过unpark唤醒
  4. interrupt:只是给线程发个信号,如果在wait, sleep会收到exception
  5. yeild:在操作系统层面让线程从Running(已经获得cpu时间片)变成Runnable状态,等待继续被调度。在jvm的线程状态还是RUNNABLE

调用修饰

表示线程在方法调用时,额外的重要的操作。线程Dump分析的重要信息。修饰上方的方法调用。

  1. locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。
  2. waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在迚入区等待。
  3. waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。
  4. parking to wait for <地址> 目标

下面举几个例子对dump信息进行分析下:

案例一:RUNNABLE状态,进入run方法之后线程的状态,以及在等待IO的时候,线程的状态。

//模拟发送http请求
Socket socket = new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, 1, 14 }), 5678));
"IOThread" #10 prio=5 os_prio=0 tid=0x00000000187c7800 nid=0x8b0 runnable [0x00000000192ee000]java.lang.Thread.State: RUNNABLEat java.net.DualStackPlainSocketImpl.connect0(Native Method)at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)- locked <0x00000000eb6c0fa8> (a java.net.DualStackPlainSocketImpl)at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)at java.net.Socket.connect(Socket.java:589)at java.net.Socket.connect(Socket.java:538)at com.test.threadpool.TestThreadState$IOThread.run(TestThreadState.java:83)

分析:虽然线程处在RUNNABLE的状态,但是还是有可能在等待IO。这样就很浪费CPU资源了,所以在使用线程池处理多线程任务的时候,如果是涉及到数据计算可以考虑使用多线程,如果是每个线程中都有http请求的操作,切不可使用多线程来做,因为网络比较慢,会导致其他线程一直得不到执行,而那边线程数却一直在上涨,最后会导致线程挤压cpu会飚高。

案例二:BLOCKED状态

模拟两个线程抢锁,当一个线程抢到锁之后进入sleep,sleep状态下不会释放锁,所以另外一个线程被阻塞。从堆栈信息可以看到,locked和waiting to lock都是同一个对象。

public static void testBlockedState() {Object lock = new Object();SleepThread t1 = new SleepThread("t1", lock);SleepThread t2 = new SleepThread("t2", lock);t1.start();t2.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread t1's state " + t1.getState());System.out.println("Thread t2's state " + t2.getState());}static class SleepThread extends Thread {private String name;private Object lock;public SleepThread(String name, Object lock) {super(name);this.name = name;this.lock = lock;}@Overridepublic void run() {System.out.println("Thread:" + name + " in run.");synchronized (lock) {System.out.println("Thread:" + name + " hold the lock.");try {Thread.sleep(1000 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread:" + name + " return the lock.");}}}
}

测试结果:

Thread:t2 in run.Thread:t1 in run.Thread:t2 hold the lock.Thread t1's state BLOCKED
Thread t2's state TIMED_WAITING

堆栈信息:

"t2" #11 prio=5 os_prio=0 tid=0x0000000018604800 nid=0x934 waiting on condition [0x000000001920f000]java.lang.Thread.State: TIMED_WAITING (sleeping)at java.lang.Thread.sleep(Native Method)at com.test.threadpool.TestThreadState$SleepThread.run(TestThreadState.java:274)- locked <0x00000000eb64b910> (a java.lang.Object)
"t1" #10 prio=5 os_prio=0 tid=0x000000001860b000 nid=0x3528 waiting for monitor entry [0x000000001910f000]java.lang.Thread.State: BLOCKED (on object monitor)at com.test.threadpool.TestThreadState$SleepThread.run(TestThreadState.java:271)- waiting to lock <0x00000000eb64b910> (a java.lang.Object)

分析:T2状态TIMED_WAITING,因为获得了锁,但是却执行了Thread.sleep(1000 * 1000)所以状态是TIMED_WAITING,至于T1,因为没有拿到锁所以处在BLOCKED状态。

WAITING状态

调用wait()方法导致的WAITING状态。

 /*** 线程调用wait方法,状态变成WAITING。*/
public static void testStateWatingByWait() {Object lock = new Object();WaitingThread waitingThread = new WaitingThread("WaitingThread", lock);waitingThread.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main thread check the state is " + waitingThread.getState() + "."); // WAITING
}static class WaitingThread extends Thread {private int timeout = 0;private Object lock;public WaitingThread(String name, Object lock) {this(name, lock, 0);}public WaitingThread(String name, Object lock, int timeout) {super(name);this.timeout = timeout;this.lock = lock;}@Overridepublic void run() {synchronized (lock) {if (timeout == 0) {try {System.out.println("Try to wait.");lock.wait();} catch (InterruptedException e) {e.printStackTrace();}} else {try {System.out.println("Try to wait in " + timeout + ".");lock.wait(timeout);} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("Over thread.");}
}

测试结果:

Try to wait.Main thread check the state is WAITING.

堆栈信息:

"WaitingThread" #10 prio=5 os_prio=0 tid=0x0000000018dea000 nid=0x1220 in Object.wait() [0x00000000198ee000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000eb6477e8> (a java.lang.Object)at java.lang.Object.wait(Object.java:502)at com.test.threadpool.TestThreadState$WaitingThread.run(TestThreadState.java:138)- locked <0x00000000eb6477e8> (a java.lang.Object)

分析:WaitingThread线程调用lock.wait();所有处在等待中,且没有指定时间。waiting on <0x00000000eb6477e8> 表示该线程使用synchronized申请对象锁成功,locked <0x00000000eb6477e8> 且成功锁住了对象 <0x00000000eb6477e8>

调用join()方法导致的WAITING状态

join()的功能就是让“主线程”等待“子线程”结束之后才能继续运行

 /*** 线程调用join方法,状态变成WAITING。*/
public static void testStateWatingByJoin() {Object lock = new Object();WaitingThread waitingThread = new WaitingThread("WaitingThread", lock);waitingThread.start();JoinThread joinThread = new JoinThread("JoinThread", waitingThread);joinThread.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main thread check the join thread's state is " + joinThread.getState() + "."); // WAITING
}static class JoinThread extends Thread {private int timeout = 0;private Thread thread;public JoinThread(String name, Thread thread) {this(name, thread, 0);}public JoinThread(String name, Thread thread, int timeout) {super(name);this.timeout = timeout;this.thread = thread;}@Overridepublic void run() {if (timeout == 0) {try {System.out.println("Try to join.");thread.join();} catch (InterruptedException e) {e.printStackTrace();}} else {try {System.out.println("Try to join in " + timeout + ".");thread.join(timeout);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Over join.");}
}
static class WaitingThread extends Thread {private int timeout = 0;private Object lock;public WaitingThread(String name, Object lock) {this(name, lock, 0);}public WaitingThread(String name, Object lock, int timeout) {super(name);this.timeout = timeout;this.lock = lock;}@Overridepublic void run() {synchronized (lock) {if (timeout == 0) {try {System.out.println("Try to wait.");lock.wait();} catch (InterruptedException e) {e.printStackTrace();}} else {try {System.out.println("Try to wait in " + timeout + ".");lock.wait(timeout);} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("Over thread.");}
}

测试结果:

Try to wait.Try to join.Main thread check the state is WAITING

堆栈信息:

"JoinThread" #11 prio=5 os_prio=0 tid=0x0000000019007000 nid=0x33c0 in Object.wait() [0x0000000019c1f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000eb64a498> (a com.test.threadpool.TestThreadState$WaitingThread)at java.lang.Thread.join(Thread.java:1252)- locked <0x00000000eb64a498> (a com.test.threadpool.TestThreadState$WaitingThread)at java.lang.Thread.join(Thread.java:1326)at com.test.threadpool.TestThreadState$JoinThread.run(TestThreadState.java:194)
"WaitingThread" #10 prio=5 os_prio=0 tid=0x0000000019006000 nid=0x35ac in Object.wait() [0x0000000019b1f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000eb64a468> (a java.lang.Object)at java.lang.Object.wait(Object.java:502)at com.test.threadpool.TestThreadState$WaitingThread.run(TestThreadState.java:138)- locked <0x00000000eb64a468> (a java.lang.Object)

分析:从打印日志可以看出WaitingThread先得到执行,但是因为该线程有lock.wait();操作,导致线程WaitingThread处在WAITING状态,转而cpu执行权给到了JoinThread,但是JoinThread线程有了thread.join();代码导致它要等待WaitingThread线程执行完才能执行,所有2个线程就这样一直WAITING下去,从堆栈信息

waiting on <0x00000000eb64a498> (a
com.test.threadpool.TestThreadState$WaitingThread)

也可以看出JoinThread在等待WaitingThread执行完。

调用LockSupport.park方法导致的WAITING状态。

使用线程池的时候经常会遇到这种状态,当线程池里面的任务都执行完毕,会等待获取任务。这个例子非常典型

public static void testStateWatingByThreadExecutor() {ExecutorService executeService = Executors.newSingleThreadExecutor();executeService.submit(new Runnable() {@Overridepublic void run() {System.out.println("Over Run.");}});try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}
}
"pool-1-thread-1" #10 prio=5 os_prio=0 tid=0x0000000018f9c000 nid=0x2e88 waiting on condition [0x0000000019aaf000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x00000000eb64cc30> (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.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)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 java.lang.Thread.run(Thread.java:748)

分析:因为线程执行完了AQS处在自旋状态,或者无线程可执行就会等待,只有线程池加入了新的线程任务,才会被唤醒。

TIMED_WAITING状态

只测试sleep()方法,其余参照WAITING状态

public static void testSleep() {try {Thread.sleep(1000 * 100);} catch (InterruptedException e) {e.printStackTrace();}
}
"main" #1 prio=5 os_prio=0 tid=0x0000000004f80800 nid=0x34bc waiting on condition [0x0000000004e7f000]java.lang.Thread.State: TIMED_WAITING (sleeping)at java.lang.Thread.sleep(Native Method)at com.test.threadpool.TestThreadState.testSleep(TestThreadState.java:233)at com.test.threadpool.TestThreadState.main(TestThreadState.java:53)

进入区等待

"d&a-3588" daemon waiting for monitor entry [0x000000006e5d5000]java.lang.Thread.State: BLOCKED (on object monitor)at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()- waiting to lock <0x0000000602f38e90> (a java.lang.Object)at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()

线程状态BLOCKED,线程动作wait on monitor entry,调用修饰waiting to
lock总是一起出现。表示在代码级别已经存在冲突的调用。必然有问题的代码,需要尽可能减少其发生。

同步块阻塞

一个线程锁住某对象,大量其他线程在该对象上等待。

"blocker" runnablejava.lang.Thread.State: RUNNABLEat com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)- locked <0x00000000eb8eff68> (a java.lang.Object)
"blockee-11" waiting for monitor entryjava.lang.Thread.State: BLOCKED (on object monitor)at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
"blockee-86" waiting for monitor entryjava.lang.Thread.State: BLOCKED (on object monitor)at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)

持续运行的IO,IO操作是可以以RUNNABLE状态达成阻塞。例如:数据库死锁、网络读写。 格外注意对IO线程的真实状态的分析。 一般来说,被捕捉到RUNNABLE的IO调用,都是有问题的。 以下堆栈显示: 线程状态为RUNNABLE。 调用栈在SocketInputStream、SocketImpl、socketRead0等方法。 调用栈包含了jdbc相关的包。很可能发生了数据库死锁,切记就算状态是RUNNABLE也可能处在阻塞状态

"d&a-614" daemon prio=6 tid=0x0000000022f1f000 nid=0x37c8 runnable [0x0000000027cbd000]java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.read(Unknown Source)at oracle.net.ns.Packet.receive(Packet.java:240)at oracle.net.ns.DataPacket.receive(DataPacket.java:92)at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:172)at oracle.net.ns.NetInputStream.read(NetInputStream.java:117)at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1034)at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:588)

分线程调度的休眠

正常的线程池等待

"d&a-131" in Object.wait()java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(Native Method)at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo(WorkingManager.java:322)- locked <0x0000000313f656f8> (a com.jiuqi.dna.core.impl.WorkingThread)at com.jiuqi.dna.core.impl.WorkingThread.run(WorkingThread.java:40)

可疑的线程等待

"d&a-121" in Object.wait()java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:485)at com.jiuqi.dna.core.impl.AcquirableAccessor.exclusive()- locked <0x00000003011678d8> (a com.jiuqi.dna.core.impl.CacheGroup)at com.jiuqi.dna.core.impl.Transaction.lock()

上面dump分析:没有等待时间一直等待下去。

看下面这个例子:

public class JStackDemo1 {public static void main(String[] args) {Thread thread = new Thread(new Thread1());thread.start();}
}
class Thread1 implements Runnable{@Overridepublic void run() {lock.wait();}
}

线程堆栈信息如下:

"Reference Handler" daemon prio=10 tid=0x00007fbbcc06e000 nid=0x286c in Object.wait() [0x00007fbbc8dfc000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x0000000783e066e0> (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:503)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)- locked <0x0000000783e066e0> (a java.lang.ref.Reference$Lock)

我们能看到:线程的状态WAITING,线程的当前锁住的资源: <0x0000000783e066e0> 线程当前等待的资源:<0x0000000783e066e0>
为什么同时锁住和等待是同一个资源:
线程的执行中,先获得了这个对象的 Monitor(对应于 locked <0x0000000783e066e0>)。当执行到 obj.wait(), 线程即放弃了Monitor的所有权,进入 “wait set”队列(对应于 waiting on <0x0000000783e066e0> )。

示范一:

下面这个线程在等待这个锁 0x00000000fe7e3b50,等待进入临界区:

"RMI TCP Connection(64896)-172.16.52.118" daemon prio=10 tid=0x00000000405a6000 nid=0x68fe waiting for monitor entry [0x00007f2be65a3000]java.lang.Thread.State: BLOCKED (on object monitor)at com.xyz.goods.service.impl.GoodsServiceImpl.findChanellGoodsCountWithCache(GoodsServiceImpl.java:1734)- waiting to lock <0x00000000fe7e3b50> (a java.lang.String)

那么谁持有这个锁呢? 是另一个先调用了findChanellGoodsCountWithCache函数的线程:

"RMI TCP Connection(64878)-172.16.52.117" daemon prio=10 tid=0x0000000040822000 nid=0x6841 runnable [0x00007f2be76b3000]java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.read(SocketInputStream.java:129)at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)at java.io.BufferedInputStream.read(BufferedInputStream.java:317)- locked <0x00000000af4ed638> (a java.io.BufferedInputStream)at org.bson.io.Bits.readFully(Bits.java:35)at org.bson.io.Bits.readFully(Bits.java:28)at com.mongodb.Response.<init>(Response.java:35)at com.mongodb.DBPort.go(DBPort.java:110)- locked <0x00000000af442d48> (a com.mongodb.DBPort)at com.mongodb.DBPort.go(DBPort.java:75)- locked <0x00000000af442d48> (a com.mongodb.DBPort)at com.mongodb.DBPort.call(DBPort.java:65)at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:202)at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:296)at com.mongodb.DB.command(DB.java:152)at com.mongodb.DBCollection.getCount(DBCollection.java:760)at com.mongodb.DBCollection.getCount(DBCollection.java:731)at com.mongodb.DBCollection.count(DBCollection.java:697)at com.xyz.goods.manager.MongodbManager.count(MongodbManager.java:202)at com.xyz.goods.service.impl.GoodsServiceImpl.findChanellGoodsCount(GoodsServiceImpl.java:1787)at com.xyz.goods.service.impl.GoodsServiceImpl.findChanellGoodsCountWithCache(GoodsServiceImpl.java:1739)- locked <0x00000000fe7e3b50> (a java.lang.String)

示范二:

等待另一个条件发生来将自己唤醒:

"RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)at java.lang.Thread.run(Thread.java:662)
  1. 1)“TIMED_WAITING (parking)”中的 timed_waiting
    指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态;parking指线程处于挂起中。

2)“waiting on condition”需要与堆栈中的“parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)”
结合来看。首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue
并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue
中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。

示范三:

"RMI RenewClean-[172.16.50.182:4888]" daemon prio=10 tid=0x0000000040d2c800 nid=0x97e in Object.wait() [0x00007f9ccafd0000]java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x0000000799b032d8> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)- locked <0x0000000799b032d8> (a java.lang.ref.ReferenceQueue$Lock)at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516)at java.lang.Thread.run(Thread.java:662)
注意以下几点
  1. wait on monitor entry:被阻塞的,肯定有问题
  2. runnable:注意IO线程
  3. in Object.wait():注意非线程池等待
  4. 虚拟机执行Full GC时,会阻塞所有的用户线程。因此,即时获取到同步锁的线程也有可能被阻塞。在查看线程Dump时,首先查看内存使用情况。

Java中有两类线程:

User Thread(用户线程)、Daemon Thread(守护线程)
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。守护线程中产生的线程也是守护线程

本文引自:https://www.cnblogs.com/myseries/p/12050083.html

Jstack查询线程堆栈相关推荐

  1. 【性能定位】使用jstack定位线程堆栈信息

    转自:https://www.javatang.com/archives/2017/10/19/33151873.html 基本概念 {#basic-info} 在对Java内存泄漏进行分析的时候,需 ...

  2. linux查看java堆栈信息_Java运行状态分析2:获取线程堆栈信息

    Java运行状态分析2:获取线程堆栈信息 基本概念 出现内存泄漏或者运行缓慢场景,有时候无法直接从业务日志看出问题时候,需要分析jvm内存和线程堆栈 线程堆栈信息主要记录jvm线程在某时刻线程执行情况 ...

  3. jstack可以定位到线程堆栈

    java命令--jstack 工具 JVM调优之jstack找出最耗cpu的线程并定位代码 jstack可以定位到线程堆栈,根据堆栈信息我们 转载于:https://www.cnblogs.com/j ...

  4. jvm(java虚拟机)线程堆栈jstack(2)

    jstack是java虚拟机自带的一种堆栈查看工具.主要目的是定位线程出现长时间停顿的原因,如线程间死锁.死循环.请求外部资源导致的长时间等待等. jstack -help Usage:jstack ...

  5. 【深入理解java虚拟机v3 】 4.2.6 jstack:Java堆栈跟踪工具(查看所有的线程信息占cpu最高的进程和线程)

    文章目录 1. 原文概述 补充概述 2. 例子 2.1 用jstack加进程id查找死锁 2.2 jstack统计线程数 2.3 jstack检测cpu高 3. 实战 3.1 一次cpu高的实战记录 ...

  6. 使用jstack查看某个Java进程内的线程堆栈信息

    本文来说下如何使用jstack来查看堆栈信息 文章目录 概述 概述

  7. java线程堆栈nid.tid_java排查一个线上死循环cpu暴涨的过程分析

    问题,打一个页面cpu暴涨,打开一次就涨100%,一会系统就卡的不行了. 排查方法,因为是线上的linux,没有用jvm监控工具rim链接上去. 只好用命令排查: top cpu排序,一个java进程 ...

  8. jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码

    jstack主要用来查看某个Java进程内的线程堆栈信息.语法格式如下: jstack [option] pid jstack [option] executable core jstack [opt ...

  9. openjdk-alpine镜像无法打印线程堆栈和内存堆栈问题

    基于openjdk:8u171-alpine构建的java镜像,使用jstack命令打印线程的时候会提示以下错误: /opt # ps -ef PID USER TIME COMMAND 1 root ...

最新文章

  1. 验证ArrayList是线程不安全的集合
  2. 动画 java_Java动画程序介绍
  3. Class Activation Mapping(CAM)
  4. Linux 设备 eth0 似乎不存在, 初始化操作将被延迟
  5. 过去式加ed的发音_过去式的变化规律,掌握诀窍了吗?
  6. fft 相位谱_FFT和示波器实用指南——深圳零式未来仪器科技
  7. php软件开发--oop(面向对象)
  8. JavaScript内置对象导读(1)
  9. JAVA学习IO(1)
  10. 用命令行查看mysql,利用命令行查看Mysql数据库
  11. bootstrap datetimepicker 日期插件
  12. 银行卡四要素验证API接口用法简介
  13. PHP入门《PHP程序设计案例教程》-- PHP语法基础
  14. linux重新分区丢失数据恢复,Linux数据恢复专题(1)——恢复丢失的分区(转载)...
  15. Pr零基础入门指南笔记一——项目、序列、预设
  16. AD620单电源应变片测量电路分析
  17. 智能交通组合拳--飞桨实现车辆类别/车牌/车速检测、跨境头跟踪、车流密度检测、逆行检测
  18. 2022年G2电站锅炉司炉考试题库及模拟考试
  19. 后台管理系统,前端框架
  20. 连接数据库失败提示hba.conf不符合的处理方法

热门文章

  1. 第二章华氏度摄氏度转换
  2. 高项_第八章项目质量管理
  3. 网络空间拟态防御CMD(Cyber Mimic Defense)
  4. 超级计算机在日常生活中有哪些有趣的应用
  5. 统计正数和负数的个数然后计算这些数的平均值。
  6. oracle索引介绍之位图(bitmap)索引
  7. ENVI-IDL中国官方微博
  8. Date int java_java.util.Calendar.set(int year, int month, int date)方法实例
  9. YOLOv5-7.0解决No module named ‘utils.datasets‘和cannot import name ‘scale_coords‘ from ‘utils.general‘
  10. VS2017离线安装失败解决无法重新安装问题 catalog问题