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。
top命令这里我们的死锁的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之间关系,以及线程的状态转换图: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: RUNNABLE
	at 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;
		}
 
		@Override
		public 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;
	}

	@Override
	public 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;
	}

	@Override
	public 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;
	}

	@Override
	public 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() {
		@Override
		public 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" runnable
	java.lang.Thread.State: RUNNABLE
	at com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)
	- locked <0x00000000eb8eff68> (a java.lang.Object)
"blockee-11" waiting for monitor entry
	java.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 entry
	java.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: RUNNABLE
	at 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{
    @Override
    public 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: RUNNABLE
	at 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

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐