背景
在实际的项目中,我们在编写一些高并发的项目的时候,经常会自己的来控制线程,但是又很容易出现问题,一旦出现问题又很难debug调试。笔者在实际中会经常起多线程来并发的执行任务,总结了一些排查多线程的问题的方法。
工具使用
如何借助一些工具来排查线程上的bug?
-
jps
: 可以查看当先系统运行了哪些java进程,同时会打印进程号。
C:\Users\robin>jps
8976 DeadLock
10588
4700 Launcher
6492 Jps
-
jstack
: 可以根据进程号来查看该进程里线程的详细状态。
jstack 8976 > d:/DeadLock.txt
-
top
: linux下查看系统资源的指令,我们可以看自己java进程的cpu和内存占用情况。
线程的状态
java 线程的状态:
NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
还有RUNNING、DEADLOCK
先来看下jdk的源码,看下官方对线程的几种状态的阐述:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
从以上的注释中我们可以发现:
NEW
: 是线程这个对象刚被new出来,还不能被执行。RUNNABLE
: 这个状态是线程已经被new出来了,并且调用了start()方法,此线程随时被执行,只要分配到cpu时间片就能进入执行状态。BLOCKED
: 这个状态是执行的线程遇到遇到同步方法(synchronized)或者同步代码块而没有得到锁,将进入阻塞状态。WAITING
: 当执行的线程调用了join() 或者 wait() 方法的时候,就会进入等待状态,直到被唤醒。TIMED_WAITING
: 这个状态就是当用了sleep(1000)或者join(1000)或者wait(1000)时,执行的线程就会进入等待状态,与WAITING不同的是,这个状态的等待是有时间限制的。TERMINATED
: 这个状态表示线程已经执行完成了。RUNNING
: 执行的中的线程的状态,官网认为正常执行中的线程是属于正常的,也就没有打印出运行中的状态。如果你想看运行中的线程信息,那么可以取看自己项目的日志信息或者控制台。DEADLOCK
: 对于这种状态,是死锁状态,一旦出现这种情况的话,需要立即更改,否则会造成意想不到的后果。
对于死锁状态比较重要,我们一定要会识别,下面笔者写了一个死锁,然后使用jstack打印出的日志:
"VM Periodic Task Thread" os_prio=2 tid=0x000000001993f000 nid=0x1c88 waiting on condition
JNI global references: 22
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00000000038cae58 (object 0x00000000d609a170, a java.util.ArrayList),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00000000038c99b8 (object 0x00000000d609a188, a java.util.ArrayList),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at DeadLock.run(DeadLock.java:37)
- waiting to lock <0x00000000d609a170> (a java.util.ArrayList)
- locked <0x00000000d609a188> (a java.util.ArrayList)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at DeadLock.run(DeadLock.java:37)
- waiting to lock <0x00000000d609a188> (a java.util.ArrayList)
- locked <0x00000000d609a170> (a java.util.ArrayList)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
可以看出,jstack的日志信息给出非常明显的提示Found one Java-level deadlock:
提示发现一个死锁,可以根据下面的信息,就能定位在哪个类,哪一行发生了死锁。