Java併發程式設計序列之執行緒狀態

我又不是架構師發表於2019-03-03

併發程式設計序列之執行緒狀態

Hello,大家好,今天開始著手寫併發程式設計序列部落格。歡迎大家訂閱點贊。談到併發程式設計,要談的東西可就多了,本文作為併發程式設計序列的第一篇文章,結構如下:

  1. 執行緒的建立方式和常見的執行緒API
  2. 執行緒的狀態
  3. 執行緒排查工具

1. 執行緒的建立方式和常見的執行緒API

  • 直接繼承Thread類 略。
  • 實現Runnable介面,傳入到Thread中去。略。
  • 通過Callable和Future建立執行緒,具有返回值:
public class CallableThreadTest implements Callable<Integer> {

    public static void main(String[] args) {
        CallableThreadTest ctt = new CallableThreadTest();
        FutureTask<Integer> ft = new FutureTask<>(ctt);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的迴圈變數i的值" + i);
            if (i == 20) {
                new Thread(ft, "有返回值的執行緒").start();
            }
        }
        try {
            System.out.println("子執行緒的返回值:" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }
} 
複製程式碼
  • 通過Executor執行緒池技術來啟動執行緒,後期講執行緒池時講。

然後提一下常見的執行緒API,說下注意點:

  1. Thread的方法為static方法,thread的方法為例項方法。
  2. Thread方法通過例項來呼叫的效果是一樣一樣的,千萬不要誤解。誤以為通過例項呼叫就是對某個例項的操作。
  3. Thread方法一般的語意就是對"當前程式碼執行執行緒"!

Thread.currentThread():獲取程式碼執行位置的當前執行緒。
thread.isAlive(); 判斷某個執行緒是否存活。
Thread.sleep(); 注意sleep方法是static方法,不管用例項呼叫,還是類呼叫,效果都是一樣,讓當前執行程式碼的執行緒sleep.
順帶說一個問題,我們在啟動執行緒時,總是呼叫start方法,start方法的效果就是內部呼叫了run方法,記得一定不要自己手動呼叫run方法,因為如果自己調的話,那麼run方法內部的Thread類的static方法中的"當前執行執行緒"的含義就不是子執行緒了,而是呼叫執行緒。
中斷相關:
thread.interrupt();將某個執行緒的中斷標誌位設定為true.注意,執行緒並沒有中斷,只是更改了標誌位。
Thread.interrupted():判斷當前執行緒是否中斷,執行完後,將標誌位更改為false,所以連續呼叫兩次,返回值有可能不一樣。 thread.isInterrupted();判斷該物件是否是中斷的,不改變標誌位。
廢棄方法,不要使用:
thread.suspend();
thread.resume();
thread.stop();

2. 執行緒的狀態

Java執行緒狀態圖

執行緒狀態 描述
NEW 初始狀態,剛建立出來還沒有start
RUNNABLE 執行中或者就緒狀態.
BLOCKED 阻塞狀態。注意只有synchronized關鍵字的鎖才會到BLOCKED狀態,JUC的Lock相關的鎖是不會到這個狀態的。而是進入到WAITING或者TIMED_WAITING狀態。因為Lock介面的相關類內部都是使用LockSupport相關的方法
WAITING/TIMED_WAITING 等待狀態。
TERMINATED 執行緒結束

3. 執行緒排查工具

先說一個概念:執行緒上下文切換,多執行緒程式設計最忌諱的就是執行緒開的特別多,然後都處於空閒狀態(WAITING),這樣CPU每次切換過去都又切換走,上下文切換比較頻繁,相當耗時。 檢視上下文切換頻率:
vmstat 1

Java併發程式設計序列之執行緒狀態
一般來說1000多是比較正常的。可以考慮使用JUC的Automic包的Cas演算法,減少上下文切換。

Jstack使用:

Java併發程式設計序列之執行緒狀態
稍微說下,先jstack生成執行緒dump檔案。然後看下有多少執行緒分別處於什麼狀態。我這裡的都還算正常的。如果有不正常的。開啟dump檔案好好看看哪裡報的問題。
grep java.lang.Thread.State /opt/dump01 | awk '{print $2$3$4$5}' | sort |uniq -c

死鎖就更有意思了,當看到BLOCKED比較多時,而且長時間不消下去。那麼就要仔細看dump檔案了,搜尋BLOCKED關鍵字,然後看是哪裡報的BLOCKED,看程式碼,檢查程式碼的死鎖。需要注意的是,如果是使用JUC的Lock造成的死鎖,那麼久要檢查WAITING狀態下的程式碼了
其次Jstack -l pid 可以列印出鎖相關的資訊。系統也可以自動幫我們偵測死鎖。類似於Found 1 deadlock這樣的關鍵字。
死鎖最有效的避免方法就是使用tryLock(time)了,拿不到鎖就返回。不要拿了。

結語

好了,打完收工。
下期預告:
執行緒間通訊,synchronized關鍵字,volatile關鍵字.

相關文章