1. 程式人生 > 其它 >java多執行緒中常用指令

java多執行緒中常用指令

------------恢復內容開始------------

一、寫在前面

    好久沒寫部落格了,這不快畢業了,應該會重新開始更新部落格了。這次主要介紹檢視執行緒狀態等一系列常見指令,包括有jps、vmstat、jstack、javap、以及如何檢視java對應的彙編程式碼。

二、情景

   依據假設情景來說明為啥以及如何使用這些指令。現在你是個初出茅廬的java程式設計師,你一看現在的業務用的單執行緒處理,大吃一斤,馬上提出我要優化,改成多執行緒,然後寫下了如下程式碼。然後你把程式碼提交,上線,準備感受多執行緒的速度。然而業務上線後直接癱瘓,這時你該怎麼辦。

package com.wx.ch1;

public class DeadLockDemo { private static String A = "A"; private static String B = "B"; public static void main(String[] args){ new DeadLockDemo().DeadLock(); } private void DeadLock(){ Thread t1 = new Thread(new Runnable() { @Override
public void run() { synchronized (A){ try { Thread.currentThread().sleep(2000); }catch (InterruptedException e){ e.printStackTrace(); } synchronized (B){ System.out.println(
"get A B"); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (B){ synchronized (A){ System.out.println("get B A"); } } } }); t1.start(); t2.start(); } }

三、發現問題

    首先,你希望你能夠獲得現在這些執行緒所處狀態,然後判斷髮生了什麼,jstack指令可以幫到你。

3.1 jstack指令

   該指令常被用於dump出指定程序下,所有執行緒的資訊。指令格式如下:

    sudo -u 使用者名稱  jstack指令位置 程序id  >檔名

   其中jstack指令位置由你安裝java的位置決定,你可以通過echo $JAVA_HOME、whereis java等命令進行查詢。檔案名錶示輸出重定向的結果,可以把結果存入對應的檔案方便檢視。

   我使用的指令樣例:

   sudo  -u wx /home/wx/java/jdk1.8/bin/jstack 32763 >/home/wx/result

我們先來看看結果,結果很明顯表明兩個執行緒都被阻塞了,互相等待對方釋放鎖。

 3.2 jps指令

   如果你按照jstack的指令嘗試打印出執行緒資訊,你會發現缺少指令中的程序id。那麼該如何查詢這些執行中的java程序id?常見的查詢程序id指令是ps,java為我們提供了

專門的查詢執行在jvm上的java程序id指令,即jps指令。

指令格式:jps

3.3 vmstat

 現在你通過上面兩個指令的組合發現了死鎖的問題,你改寫了程式碼,成功執行在伺服器上了,下一步你要做的是精益求精,開始優化!當然優化是件很複雜的事情,這邊只提通過檢視切換上下文次數的指令vmstat來判斷當前多執行緒程式碼執行情況。

指令格式:vmstat (後面可以接引數 可自行vmstat -h 檢視)

最常見的為 vmstat -t 1 (每間隔一秒時間列印一次)

vmstat -t 1

 

 

 圖中cs(Content Swich)列為即為上下文切換次數。

3.4 javap &  java -XX:+UnlockDiagnosticVMOptions   -XX:+PrintAssembly

這兩個指令被用於檢視對應的java位元組碼以及彙編程式碼。在本文的場景中,我們通過javap以及虛擬機器啟動命令列引數,檢視並驗證volatile關鍵字的底層實現。

在多個材料中均提及帶有volatuke關鍵字的變數在修改時,對應生成的彙編程式碼帶有lock字首,我們要做的就是驗證這一點。

驗證程式碼:

package com.wx.ch1;

public class RecordExample {
    int a =0;
    volatile  boolean flag = false;

    public void writer(){
        flag = true;

        for(int i=0;i<1000000;i++){
        }
        a = 1;
    }

    public boolean reader(){
        if(flag){
            int i = a*a;
            System.out.println(i);
            return  true;
        }
        return  false;
    }

    public static void main(String[] args) {
        RecordExample re = new RecordExample();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                re.writer();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    if (re.reader()){
                        break;
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

程式碼的邏輯無需在意,只需要關注該程式碼對於volatile宣告的變數flag進行了修改。

通過指令:javap -v RecordExample(視具體路徑需要補充包名)

得到關鍵位元組碼如下,可以看到flags變數宣告帶有ACC_VOLATILE標識,該標識對應於jvm中的原始碼方法,該方法負責生成適配不同機器的作業系統,轉換成同等語義的彙編程式碼,即在該程式碼中,該位元組碼對應的

彙編程式碼,被插入了lock字首的指令。

 

 

 通過虛擬機器指令檢視進一步檢視位元組碼對應的彙編程式碼

java -XX:+UnlockDiagnosticVMOptions   -XX:+PrintAssembly  類名

在實際使用該指令檢視彙編程式碼的過程中,遇到了不少問題。

1)獲得的彙編程式碼為16進位制格式,無法理解程式碼含義。經過判斷認為是jdk版本的原因導致,之前所使用的版本為open-jdk17,在替換成oracle-jdk1.8以後,可以見到明文形式的彙編程式碼。

2)在更換完版本以後執行該指令發現缺少對應的元件hsdis-amd64.so。如果沒有下載並新增該元件,執行指令時會顯示can not load hsdis-amd64.so。該元件是ubuntu下的格式,windows下的版本不同。windows版本網路上

很多,直接搜尋hsdis dll關鍵字。這裡提供一下linux上64位的該元件下載地址:連結: https://pan.baidu.com/s/1FkDyXDhoPk3XLfsNx3U3Rw 提取碼: 1ta3 複製這段內容後開啟百度網盤手機App,操作更方便哦。

3)完成下載以後,在linux作業系統中需要將該檔案貼上複製到java安裝目錄下的/jre/lib/amd64,我的貼上路徑為:/home/wx/java/jdk1.8/jre/lib/amd64。同意可以通過echo $JAVA_HOME查詢jdk安裝目錄,前提是配置了環境變數。

在完成上述一系列操作以後,就可以在控制檯敲上述指令觀察得到對應的彙編程式碼。在這裡我們通過開發工具idea完成相關虛擬機器指令配置,直接在idea中輸出對應的彙編程式碼。

對應配置截圖如下

 

 如果沒有虛擬機器的配置項,則點選modify options增加vm配置 ,勾選add VM options:

 

 生成彙編程式碼截圖:可以看到對應指令帶有的lock字首,註解部分顯示是操作volatile變數。

 

 接著我們將程式碼flag變數宣告的volatile刪除,宣告為一般變數,再次檢視對應的彙編程式碼,會發現不再有帶有lock字首的對於變數值修改的指令。

四、最後

  總結一下,介紹了與多執行緒程式設計相關的一些基礎指令,首先通過jps配合jstack可以檢視當前執行緒的狀態資訊;接著,可以通過vmstat指令去檢視執行緒的導致的上下文切換次數,該指標是反應多執行緒效能的一個重要指標,過多的上下文切換會影響執行緒的執行速度。最後,介紹了檢視java對應位元組碼與彙編程式碼的指令。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

------------恢復內容結束------------