java thread的運行週期中, 有幾種狀態, 在 java.lang.Thread.State 中有詳細定義和說明: java
NEW 狀態是指線程剛建立, 還沒有啓動 併發
RUNNABLE 狀態是線程正在正常運行中, 固然可能會有某種耗時計算/IO等待的操做/CPU時間片切換等, 這個狀態下發生的等待通常是其餘系統資源, 而不是鎖, Sleep等 異步
BLOCKED 這個狀態下, 是在多個線程有同步操做的場景, 好比正在等待另外一個線程的synchronized 塊的執行釋放, 或者可重入的 synchronized塊裏別人調用wait() 方法, 也就是這裏是線程在等待進入臨界區
WAITING 這個狀態下是指線程擁有了某個鎖以後, 調用了他的wait方法, 等待其餘線程/鎖擁有者調用 notify / notifyAll 一遍該線程能夠繼續下一步操做, 這裏要區分 BLOCKED 和 WATING 的區別, 一個是在臨界點外面等待進入, 一個是在理解點裏面wait等待別人notify, 線程調用了join方法 join了另外的線程的時候, 也會進入WAITING狀態, 等待被他join的線程執行結束 jvm
TIMED_WAITING 這個狀態就是有限的(時間限制)的WAITING, 通常出如今調用wait(long), join(long)等狀況下, 另一個線程sleep後, 也會進入TIMED_WAITING狀態 ide
TERMINATED 這個狀態下表示 該線程的run方法已經執行完畢了, 基本上就等於死亡了(當時若是線程被持久持有, 可能不會被回收) 工具
下面談談如何讓線程進入以上幾種狀態:
1. NEW, 這個最簡單了,
static void NEW() {
Thread t = new Thread ();
System. out.println(t.getState());
}
輸出NEW
2. RUNNABLE, 也簡單, 讓一個thread start, 同時代碼裏面不要sleep或者wait等
private static void RUNNABLE() {
Thread t = new Thread(){
public void run(){
for(int i=0; i<Integer.MAX_VALUE; i++){
System. out.println(i);
}
}
};
t.start();
}
3. BLOCKED, 這個就必須至少兩個線程以上, 而後互相等待synchronized 塊
private static void BLOCKED() {
final Object lock = new Object();
Runnable run = new Runnable() {
@Override
public void run() {
for(int i=0; i<Integer.MAX_VALUE; i++){
synchronized (lock) {
System. out.println(i);
}
}
}
};
Thread t1 = new Thread(run);
t1.setName( 「t1」);
Thread t2 = new Thread(run);
t2.setName( 「t2」);
t1.start();
t2.start();
}
這時候, 一個在RUNNABLE, 另外一個就會在BLOCKED (等待另外一個線程的 System.out.println.. 這是個IO操做, 屬於系統資源, 不會形成WAITING等)
4. WAITING, 這個須要用到生產者消費者模型, 當生產者生產過慢的時候, 消費者就會等待生產者的下一次notify
private static void WAITING() {
final Object lock = new Object();
Thread t1 = new Thread(){
@Override
public void run() {
int i = 0;
while(true ){
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
}
System. out.println(i++);
}
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
while(true ){
synchronized (lock) {
for(int i = 0; i< 10000000; i++){
System. out.println(i);
}
lock.notifyAll();
}
}
}
};
t1.setName( 「^^t1^^」);
t2.setName( 「^^t2^^」);
t1.start();
t2.start();
}
5. TIMED_WAITING, 這個僅須要在4的基礎上, 在wait方法加上一個時間參數進行限制就OK了.
把4中的synchronized 塊改爲以下就能夠了.
synchronized
(lock) {
try {
lock.wait(60 * 1000L);
}
catch
(InterruptedException e) {
}
System. out .println(i++);
}
另外看stack的輸出, 他叫 TIMED_WAITING(on object monitor) , 說明括號後面還有其餘的狀況, 好比sleep, 咱們直接把t2的for循環改爲sleep試試:
synchronized (lock) {
try {
sleep(30*1000L);
} catch (InterruptedException e) {
}
lock.notifyAll();
}
看到了吧, t2的state是 TIMED_WAITING( sleeping), 而t1依然是on object monitor , 由於t1仍是wait在等待t2 notify, 而t2是本身sleep
另外, join操做也是進入 on object monitor
6. TERMINATED, 這個狀態只要線程結束了run方法, 就會進入了…
private static void TERMINATED() {
Thread t1 = new Thread();
t1.start();
System. out.println(t1.getState());
try {
Thread. sleep(1000L);
} catch (InterruptedException e) {
}
System. out.println(t1.getState());
}
輸出:
RUNNABLE
TERMINATED
因爲線程的start方法是異步啓動的, 因此在其執行後當即獲取狀態有可能纔剛進入RUN方法且還未執行完畢
廢話了這麼多, 瞭解線程的狀態究竟有什麼用?
因此說這是個釣魚貼麼…
好吧, 一句話, 在找到系統中的潛在性能瓶頸有做用.
當java系統運行慢的時候, 咱們想到的應該先找到性能的瓶頸, 而jstack等工具, 經過jvm當前的stack能夠看到當前整個vm全部線程的狀態, 當咱們看到一個線程狀態常常處於
WAITING 或者 BLOCKED的時候, 要當心了, 他可能在等待資源常常沒有獲得釋放(固然, 線程池的調度用的也是各類隊列各類鎖, 要區分一下, 好比下圖)
這是個經典的併發包裏面的線程池, 其調度隊列用的是LinkedBlockingQueue, 執行take的時候會block住, 等待下一個任務進入隊列中, 而後進入執行, 這種理論上不是系統的性能瓶頸, 找瓶頸通常先找本身的代碼stack,再去排查那些開源的組件/JDK的問題
排查問題的幾個思路:
0. 如何跟蹤一個線程?
看到上面的stack輸出沒有, 第一行是內容是 threadName priority tid nid desc
更過跟蹤tid, nid 均可以惟一找到該線程.
1. 發現有線程進入BLOCK, 並且持續很久, 這說明性能瓶頸存在於synchronized塊中, 由於他一直block住, 進不去, 說明另外一個線程一直沒有處理好, 也就這個synchronized塊中處理速度比較慢, 而後再深刻查看. 固然也有可能同時block的線程太多, 排隊過久形成.
2. 發現有線程進入WAITING, 並且持續很久, 說明性能瓶頸存在於觸發notify的那段邏輯. 固然還有就是同時WAITING的線程過多, 總是等不到釋放.
3. 線程進入TIME_WAITING 狀態且持續很久的, 跟2的排查方式同樣.
上面的黑底白字截圖都是經過jstack打印出來的, 能夠直接定位到你想知道的線程的執行棧, 這對java性能瓶頸的分析是有極大做用的.
NOTE: 上面全部代碼都是爲了跟蹤線程的狀態而寫的, 千萬不要在線上應用中這麼寫…