java線程狀態:
- 初始(NEW):新創建了一個線程對象,但還沒有調用start()方法。
- 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱為“運行”。
線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位于可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處于就緒狀態(ready)。就緒狀態的線程在獲得CPU時間片后變為運行中狀態(running)。 - 阻塞(BLOCKED):表示線程阻塞于鎖。
- 等待(WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。
- 超時等待(TIMED_WAITING):該狀態不同于WAITING,它可以在指定的時間后自行返回。
- 終止(TERMINATED):表示該線程已經執行完畢。
Java Monitor原理:
Monitor 是Java中用以實現線程之間的互斥與協作的主要手段,可以看成是對象或者Class的鎖。每一個對象有且僅有一個monitor。Java 的 Object 類本身就是監視者對象,Java 對于 Monitor Object 模式做了內建的支持。
Object 類本身就是監視者對象!
每個 Object 都帶了一把看不見的鎖,通常叫 內部鎖/Monitor 鎖/Instrinsic Lock, 這把鎖就是 監控鎖。
synchronized 關鍵字修飾方法和代碼塊就是 同步方法
wait()/notify()/notifyAll() 方法構成監控條件(Monitor Condition)
線程和Monitor之間關系:
一個Monitor在某個時刻,只能被一個線程擁有,該線程就是Active Thread,
而其它線程都是Waiting Thread,分別在兩個隊列Entry Set和Wait Set里面等候。
在Entry Set中等待的線程狀態是Waiting for monitor entry,
在Wait Set中等待的線程狀態是in Object.wait()。
線程進入同步方法中:
為了繼續執行臨界區代碼,線程必須獲取 Monitor 鎖。如果獲取鎖成功,將成為該監視者對象的擁有者。任一時刻內,監視者對象只屬于一個活動線程(The Owner)
擁有監視者對象的線程可以調用 wait() 進入等待集合(Wait Set),同時釋放監視鎖,進入等待狀態。
其他線程調用 notify() / notifyAll() 接口喚醒等待集合中的線程,這些等待的線程需要重新獲取監視鎖后才能執行 wait() 之后的代碼。
同步方法執行完畢了,線程退出臨界區,并釋放監視鎖。
進入區(Entrt Set):表示線程通過synchronized要求獲取對象的鎖。如果對象未被鎖住,則迚入擁有者;否則則在進入區等待。一旦對象鎖被其他線程釋放,立即參與競爭。
擁有者(The Owner):表示某一線程成功競爭到對象鎖。
等待區(Wait Set):表示線程通過對象的wait方法,釋放對象的鎖,并在等待區等待被喚醒。
線程調用修飾:
表示線程在方法調用時,額外的重要的操作,一般出現在線程內容中,用來協助分析線程的狀態。
locked <對象地址> 對象名: 表示當前線程通過synchronized關鍵字成功獲取到了目標對象的監視器,擁有了在臨界區操作的權限,狀態一般為runnable。注意對象鎖是可重入的,所以這里有2次鎖住了同一個對象。
waiting to lock <對象地址> 對象名:
表示當前線程未能通過synchronized關鍵字獲取到目標對象的監視器,狀態一般為blocked。
waiting on<對象地址> 對象名:
表示當前線程通過synchronized關鍵字成功獲取到了目標對象的監視器,但調用了wait方法,進入對象的等待區等待。在調用棧頂出現,線程狀態為WAITING或TIMED_WATING。
parking to wait for <對象地址> 對象名:
park是基本的線程阻塞原語,不通過監視器在對象上阻塞。隨concurrent包會出現的新的機制,與synchronized體系不同,具體介紹可以看stackoverflow上的回答傳送門。
系統線程狀態:
出現在線程信息第一行的末尾。
deadlock:死鎖線程
runnable:表示線程運行中或I/O等待,線程狀態一般為RUNNABLE。
in Object.wait():進入臨界區之后,又調用了java.lang.Object.wait()方法等待,jvm線程狀態一般為WAITING或TIMED_WAITING。
waiting for monitor entry:在等待進入一個臨界區,線程狀態一般狀態為BLOCKED。
waiting on condition:線程正處于等待資源或等待某個條件的發生,具體的原因需要結合下面堆棧信息進行分析。
(1)如果堆棧信息明確是應用代碼,則證明該線程正在等待資源,一般是大量讀取某種資源且該資源采用了資源鎖的情況下,線程進入等待狀態,等待資源的讀取,或者正在等待其他線程的執行等。
(2)如果發現有大量的線程都正處于這種狀態,并且堆棧信息中得知正等待網絡讀寫,這是可能因為網絡阻塞導致線程無法執行。
(3)還有一種常見的情況是該線程在sleep,等待sleep的時間到了,將被喚醒。
JVM線程狀態:
java.lang.Thread.State: RUNNABLE 線程運行中或I/O等待 方法調用-無
java.lang.Thread.State: BLOCKED (on object monitor) 等待進入一個臨界區 方法調用-synchronized
java.lang.Thread.State: TIMED_WAITING (parking) 線程等待喚醒,并且設置等待時長 方法調用-LockSupport.parkNanos(等待時長)、LockSupport.parkUntil(等待時長)
java.lang.Thread.State: TIMED_WAITING (sleeping) 線程等待喚醒,并且設置等待時長 方法調用-Thread.sleep(等待時長),Thread.join(等待時長)
java.lang.Thread.State: TIMED_WAITING (on object monitor) 線程在等待喚醒,并且設置等待時長 方法調用-Object.wait(等待時長)
java.lang.Thread.State: WAITING (parking) 線程等待喚醒,沒有設置等待時長 方法調用-LockSupport.park()
java.lang.Thread.State: WAITING (on object monitor) 線程等待喚醒,并且沒有設置等待時長 方法調用-Object.wait()
java.lang.Thread.State: WAITING (on object monitor) 線程等待喚醒,沒有設置等待時長 方法調用-Thread.join()
jvm日志分析:
“ForkJoinPool.commonPool-worker-52”{線程名} #5688{線程ID} daemon prio=5{線程的優先級,取值范圍為[1-10],默認為 5,數值越低越有優先} os_prio=0{線程在操作系統中的優先級} tid=0x00007f31eb3f3800 nid=0xa8ec{Native thread ID,本地操作系統相關的線程id,對應JVM 虛擬機中線程映射在操作系統中的線程編號。(這個nid就是我們用top -Hp pid 查看到的線程號,不過要用printf "%x\n"轉換一下進制)} waiting on condition [0x00007f31944d0000]{系統線程狀態}
java.lang.Thread.State: WAITING (parking){JVM線程狀態}
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000004706e6598> (a java.util.concurrent.ForkJoinPool)
at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Locked ownable synchronizers:
- None
常見問題:
1)鎖爭用
如果出現大量的鎖爭用,可能是線程阻塞了!
#48線程獲得了 <0x000000046b9b6078>對象的鎖,進入了臨界區;#49無法獲取到鎖,停留在Entry Set中,輸出waiting to lock <0x000000046b9b6078>。
“hiveserver2-web-49-acceptor-2@23302924-ServerConnector@6d6d480c{HTTP/1.1,[http/1.1]}{0.0.0.0:10002}” #49 daemon prio=3 os_prio=0 tid=0x00007f31eaf33000 nid=0x6306 waiting for monitor entry [0x00007f31a3dfc000]
java.lang.Thread.State: BLOCKED (on object monitor) --JVM線程狀態
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:234)
- waiting to lock <0x000000046b9b6078> (a java.lang.Object)
at org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:397)
at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:601)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
“hiveserver2-web-48-acceptor-1@580fe4f0-ServerConnector@6d6d480c{HTTP/1.1,[http/1.1]}{0.0.0.0:10002}” #48 daemon prio=3 os_prio=0 tid=0x00007f31eaf31800 nid=0x6305 runnable [0x00007f31a3efd000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x000000046b9b6078> (a java.lang.Object)
at org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:397)
at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:601)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
2)死鎖
如果兩個線程相互都被對方的線程鎖鎖住,這樣就造成了死鎖。
Thread1獲取了<0x00000000d8251168>鎖,等待<0x00000000d8251198>鎖;Thread2獲取了<0x00000000d8251198>鎖,等待<0x00000000d8251168>鎖。導致死鎖!
“Thread2” daemon prio=5 tid=0x00007f1d3825f800 nid=0x2142 waiting for monitor entry [0x00007f1d16eeb000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jvm.study.threaddump.deadlock.DeadLockMock$2.run(DeadLockMock.java:31)
- waiting to lock <0x00000000d8251168> (a java.lang.String)
- locked <0x00000000d8251198> (a java.lang.String)
“Thread1” daemon prio=5 tid=0x00007f1d3825e000 nid=0x2141 waiting for monitor entry [0x00007f1d16fec000]
ava.lang.Thread.State: BLOCKED (on object monitor)
at com.jvm.study.threaddump.deadlock.DeadLockMock$1.run(DeadLockMock.java:16)
- waiting to lock <0x00000000d8251198> (a java.lang.String)
- locked <0x00000000d8251168> (a java.lang.String)
3)等待區等待
JVM線程的狀態是java.lang.Thread.State: TIMED_WAITING (on object monitor),線程調用了java.lang.Object.wait(Native Method)方法而進入了等待狀態。
"Wait Set"中等待的線程狀態就是 in Object.wait();
當線程獲得了Monitor進入臨界區之后,如果發現線程繼續運行的條件沒有滿足,它就調用對象(通常是被synchronized的對象)的wait()方法,放棄了Monitor,進入"Wait Set"隊列中。
只有當別的線程在該對象上調用了notify()或notifyAll()方法, “Wait Set” 隊列中線程才得到機會去競爭,但是只有一個線程獲得對象的 Monitor,從而恢復到運行態。
“IPC Client (2108708444) connection to IT-CDH-Node02/10.11.16.36:8020 from hive” #5681 daemon prio=5 os_prio=0 tid=0x00007f31ccf4a000 nid=0xa8e4 in Object.wait() [0x00007f319aa33000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at org.apache.hadoop.ipc.ClientConnection.waitForWork(Client.java:1017)
- locked <0x000000042b52dbf0> (a org.apache.hadoop.ipc.Client$Connection)
at org.apache.hadoop.ipc.ClientConnection.run(Client.java:1061)
Locked ownable synchronizers:
- None