背景
在實(shí)際的項(xiàng)目中,我們?cè)诰帉懸恍└卟l(fā)的項(xiàng)目的時(shí)候滩援,經(jīng)常會(huì)自己的來控制線程栅隐,但是又很容易出現(xiàn)問題,一旦出現(xiàn)問題又很難debug調(diào)試玩徊。筆者在實(shí)際中會(huì)經(jīng)常起多線程來并發(fā)的執(zhí)行任務(wù)租悄,總結(jié)了一些排查多線程的問題的方法。
工具使用
如何借助一些工具來排查線程上的bug?
-
jps
: 可以查看當(dāng)先系統(tǒng)運(yùn)行了哪些java進(jìn)程恩袱,同時(shí)會(huì)打印進(jìn)程號(hào)泣棋。
C:\Users\robin>jps
8976 DeadLock
10588
4700 Launcher
6492 Jps
-
jstack
: 可以根據(jù)進(jìn)程號(hào)來查看該進(jìn)程里線程的詳細(xì)狀態(tài)。
jstack 8976 > d:/DeadLock.txt
-
top
: linux下查看系統(tǒng)資源的指令畔塔,我們可以看自己java進(jìn)程的cpu和內(nèi)存占用情況潭辈。
線程的狀態(tài)
java 線程的狀態(tài):
NEW、RUNNABLE澈吨、BLOCKED把敢、WAITING、TIMED_WAITING谅辣、TERMINATED
還有RUNNING修赞、DEADLOCK
先來看下jdk的源碼,看下官方對(duì)線程的幾種狀態(tài)的闡述:
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;
}
從以上的注釋中我們可以發(fā)現(xiàn):
NEW
: 是線程這個(gè)對(duì)象剛被new出來桑阶,還不能被執(zhí)行柏副。RUNNABLE
: 這個(gè)狀態(tài)是線程已經(jīng)被new出來了,并且調(diào)用了start()方法蚣录,此線程隨時(shí)被執(zhí)行割择,只要分配到cpu時(shí)間片就能進(jìn)入執(zhí)行狀態(tài)。BLOCKED
: 這個(gè)狀態(tài)是執(zhí)行的線程遇到遇到同步方法(synchronized)或者同步代碼塊而沒有得到鎖萎河,將進(jìn)入阻塞狀態(tài)荔泳。WAITING
: 當(dāng)執(zhí)行的線程調(diào)用了join() 或者 wait() 方法的時(shí)候蕉饼,就會(huì)進(jìn)入等待狀態(tài),直到被喚醒换可。TIMED_WAITING
: 這個(gè)狀態(tài)就是當(dāng)用了sleep(1000)或者join(1000)或者wait(1000)時(shí)椎椰,執(zhí)行的線程就會(huì)進(jìn)入等待狀態(tài)厦幅,與WAITING不同的是沾鳄,這個(gè)狀態(tài)的等待是有時(shí)間限制的。TERMINATED
: 這個(gè)狀態(tài)表示線程已經(jīng)執(zhí)行完成了确憨。RUNNING
: 執(zhí)行的中的線程的狀態(tài)译荞,官網(wǎng)認(rèn)為正常執(zhí)行中的線程是屬于正常的,也就沒有打印出運(yùn)行中的狀態(tài)休弃。如果你想看運(yùn)行中的線程信息吞歼,那么可以取看自己項(xiàng)目的日志信息或者控制臺(tái)。DEADLOCK
: 對(duì)于這種狀態(tài)塔猾,是死鎖狀態(tài)篙骡,一旦出現(xiàn)這種情況的話,需要立即更改丈甸,否則會(huì)造成意想不到的后果糯俗。
對(duì)于死鎖狀態(tài)比較重要,我們一定要會(huì)識(shí)別睦擂,下面筆者寫了一個(gè)死鎖得湘,然后使用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:
提示發(fā)現(xiàn)一個(gè)死鎖顿仇,可以根據(jù)下面的信息淘正,就能定位在哪個(gè)類,哪一行發(fā)生了死鎖臼闻。