標籤:jmeter源碼 多線程 安全執行緒 java
筆者最近在看jmeter源碼,對多執行緒部分的瞭解記錄如下。
Part1 線程與多線程概念
提到線程先來看一下進程(線程的容器)的概念,進程是一個具有獨立功能的程式關於某個資料集合的一次運行活動。它可以申請和擁有系統資源,是一個動態概念,是一個活動的實體。它不只是程式的代碼,還包括當前的活動,通過程式計數器的值和處理寄存器的內容來表示。
對於jmeter來說,運行中的jmeter程式執行個體便是一個進程。而該進程中會包含大量線程。
線程是程式執行流的最小單位,是一組命令的集合。在jmeter中一個線程可以用來執行一個測試案例。在起停等基本屬性設定的基礎上,線程運行時會根據testtree解析出的sample取樣器按需執行流程中包含的測試請求。
不同的資料中對線程狀態有不完全相同的描述,基本上分為五種狀態:建立 開始(等待) 運行 掛起 和 停止。
在jmeter/壓測寶中,需要有高並發性,大量的線程並存執行,其中每個線程代表一個VU。這就涉及到多線程的概念。
對於多線程,在java中有兩種實現方式,即,一種是通過繼承thread類;另一種是實現Runnable介面。由於java單繼承機制,繼承thread實現有更多的局限性,一般採用Runnable介面方式。同時,實現了Runnable介面的類,可以通過thread類構造方法public Thread(Runnable target) 來轉化運行。
Jmeter中線程的實現也是採用了Runnable介面方式。
並發大量線程執行相同任務,需要統一管理配置多線程的狀態,便需要引入線程組(threadgroup)的概念。
<Ps:實現Runnable介面比繼承Thread類所具有的優勢:
1):適合多個相同的程式碼的線程去處理同一個資源
2):可以避免java中的單繼承的限制
3):增加程式的健壯性,代碼可以被多個線程共用,代碼和資料獨立。>
Part2:線程組
Java中有threadgroup的概念,ThreadGroup是一個類,它的目的是提供關於線程組的資訊。ThreadGroup API比較薄弱,它並沒有比Thread提供了更多的功能。它有兩個主要的功能:一是擷取線程組中處於活躍狀態線程的列表;二是為線程設定捕獲異常處理器。
而這薄弱的API不能滿足JMeter的需求,所以JMeter是自訂的threadgroup類。用來實現對一組線程(也就是一個測試執行個體的並發線程)的管理。它針對線程組的各項屬性,如線程的延時啟動時間/開始時間/結束時間/線程總數/正在啟動並執行線程數/線程啟動時間間隔進行設定。當有線程延時啟動時,先建立等量守護線程,由守護線程根據時間建立對應的使用者線程並依次啟動;沒有延時啟動時,直接建立使用者線程並依次啟動。
另外,線程組提供scheduleThread(This will schedule the time for the JMeterThread.),對線程啟動進行時間安排;並能夠控制線程的相關方法:啟動 暫停 停止 等待停止線程, 擷取活動的線程數等。
上面提到,實現runnable介面的方式適合多個相同的程式碼的線程去處理同一個資源。多線程共用資源便容易引起安全執行緒問題。
Part3:安全執行緒
安全執行緒問題產生原理:
線程的工作原理,jvm有一個main memory,而每個線程有自己的working memory,一個線程對一個variable進行操作時,都要在自己的working memory裡面建立一個copy,操作完之後再寫入main memory。多個線程同時操作同一個variable,就可能會出現不可預知的結果。
比如,兩個線程同時為一個數組添加項,當前數組長度為0.兩個線程同時對其操作,會分別在自己的工作記憶體中拷貝一份進行添加。兩線程分別添加完返回,此時數組長度已經是2,但是每個線程返回的數組長度依然是1,這就引發了問題。
Jmeter中保證安全執行緒的方式:
一)在start方法執行之前定義好線程內部的變數;
Eg :
啟動時間 終止時間等參數是通過第一種賦值,如下jmeter源碼描述:
The following variables are set by StandardJMeterEngine.
This is done before start() is called, so the values will be published to the thread safely
線程的變數是在start方法執行之前,這樣保證變數的作用範圍僅限於線程內。
二)用volatile關鍵字來標識線程共用的變數(PS:當我們使用volatile關鍵字去修飾變數的時候,所以線程都會直接讀取該變數並且不緩衝它);
Eg:
線程共用的變數如running /onErrorStopTest等是採用volatile關鍵字。
三)通過synchronized同步代碼塊或方法體(PS:用synchronized的關鍵是建立一個monitor,這個monitor可以是要修改的variable也可以其他你認為合適的object比如method,然後通過給這個monitor加鎖來實現安全執行緒,每個線程在獲得這個鎖之後,要執行完 從mainmemory load到workingmemory -> use&assign -> store到mainmemory 的過程,才會釋放它得到的鎖。這樣就實現了所謂的安全執行緒。);
Eg:
對於方法/代碼塊,如 static synchronized void incrNumberOfThreads()新增活動線程方法等,多線程共用的代碼,需要用synchronized 進行同步。
筆者對多線程瞭解尚淺,文中不當之處敬請指正,萬分感謝!
java多線程&&Jmeter壓測實現