1. 程式人生 > >測試併發應用(九)MultithreadedTC測試併發程式碼

測試併發應用(九)MultithreadedTC測試併發程式碼

宣告:本文是《 Java 7 Concurrency Cookbook 》的第八章, 作者: Javier Fernández González 譯者:鄭玉婷

MultithreadedTC測試併發程式碼

MultithreadedTC 是一個 Java 庫用來測試併發應用。它的主要目的是為了解決併發應用的不確定的問題。你不能控制他們的執行順序。為了這個目睹,它包含了內部節拍器來控制應用的不同執行緒的執行順序。這些測試執行緒作為類的方法來實現的。

在這個指南,你將學習如何使用 MultithreadedTC 庫來為LinkedTransferQueue 實現一個測試。

準備
你必須從 http://code.google.com/p/ multithreadedtc/ 下載 MultithreadedTC library,並從 http://www.junit.org/ 下載 JUnit library, Version 4.10。把 junit-4.10.jar 和 MultithreadedTC-1.01.jar檔案加入專案的庫中。

怎麼做呢…

按照這些步驟來實現下面的例子::

//1.   建立一個類,名為 ProducerConsumerTest,擴充套件 MultithreadedTestCase 類。
public class ProducerConsumerTest extends MultithreadedTestCase {

//2.   宣告一個私有 LinkedTransferQueue 屬性,用 String 類為引數,名為 queue。
private LinkedTransferQueue<String> queue;

//3.   實現 initialize() 方法。此方法不接收任何引數,也不返回任何值。它呼叫父類的 initialize() 方法,然後初始化 queue 屬性。
@Override
public void initialize() {
	super.initialize();
	queue=new LinkedTransferQueue<String>();
	System.out.printf("Test: The test has been initialized\n");
}

//4.   實現 thread1() 方法。它將實現的邏輯是第一個consumer。呼叫 queue 的 take() 方法,然後把返回值寫入操控臺。
public void thread1() throws InterruptedException {
	String ret=queue.take();
	System.out.printf("Thread 1: %s\n",ret);
}

//5.   實現 thread2() 方法。它將實現的邏輯是第二個consumer。首先,使用 waitForTick() 方法,一直等到第一個執行緒在 take() 方法中進入休眠。然後,呼叫queue的 take() 方法,並把返回值寫入操控臺。
public void thread2() throws InterruptedException {
	waitForTick(1);
	String ret=queue.take();
	System.out.printf("Thread 2: %s\n",ret);
}

//6.   實現 thread3() 方法。它將實現的邏輯是producer。 首先,使用 waitForTick() 兩次一直等到2個consumers被阻塞。然後,呼叫 queue的 put() 方法插入2個String 到queue中。
public void thread3() {
	waitForTick(1);
	waitForTick(2);
	queue.put("Event 1");
	queue.put("Event 2");
	System.out.printf("Thread 3: Inserted two elements\n");
}

//7.	最後,實現 finish() 方法。寫資訊到操控臺表明測試結束執行。使用assertEquals() 方法檢查2個事件已經被consumed(queue的大小為0)。
public void finish() {
	super.finish();
	System.out.printf("Test: End\n");
	assertEquals(true, queue.size()==0);
	System.out.printf("Test: Result: The queue is empty\n");
}

//8.   建立例子的主類通過建立一個類,名為 Main 並新增 main()方法。
public class Main {

public static void main(String[] args) throws Throwable {

//9.   建立 ProducerConsumerTest 物件,名為 test。
ProducerConsumerTest test=new ProducerConsumerTest();

//10. 使用 TestFramework 類的 runOnce()方法來執行測試。
System.out.printf("Main: Starting the test\n");
TestFramework.runOnce(test);
System.out.printf("Main: The test has finished\n");

它是如何工作的…

在這個指南,你使用MultithreadedTC庫為 LinkedTransferQueue 類實現了一個測試。你可以使用這個庫和它的節拍器為任何併發應用或者類實現測試。在例子中,你實現經典的 producer/ consumer 問題,2個consumers 和一個producer。 你想要測試的是 在buffer裡的第一個介紹的 String 物件會被第一個consumer 消耗,和在buffer裡第二個介紹的String物件會被第二個到達的consumer消耗。

MultithreadedTC庫是基於JUnit 庫的。JUnit庫是在Java中最經常用來實現unit測試時使用的庫。使用 MultithreadedTC 庫來實現一個基本測試,你必須擴充套件 MultithreadedTestCase 類。這個類擴充套件了 junit.framework.AssertJUnit 類,包含了全部檢查測試結果的方法。它並沒有擴充套件 junit.framework.TestCase 類,使用你不能在其他 JUnit 測試中匯入MultithreadedTC測試。

然後,你實現了以下這些方法:

initialize(): 此方法的實現是可選的。當你開始測試時,它就會執行,為了初始化測試中使用的物件而使用它。
finish(): 此方法的實現是可選的。當測試結束時,它就會執行。你可以使用它在測試或者檢查測試結果期間來關閉或者釋放資源。
實現測試的方法:這些方法的主要邏輯就是測試你的實現。他們用thread作為始關鍵詞連線著字串 例如, thread1()。

使用 waitForTick() 方法來控制執行緒的執行順序。此方法接收一個整數type作為引數,把正在執行的thread放入休眠直到全部在測試裡執行的執行緒都被阻塞。等他們被阻塞時, MultithreadedTC 庫呼叫 waitForTick() 方法 恢復被阻塞的執行緒。

傳遞給 waitForTick() 方法作為引數的整數是用來控制執行順序的。MultithreadedTC 庫的節拍器有個內部計數器。當全部執行緒被阻塞時,庫增加計數器到下個在 waitForTick() 呼叫中的數字,那麼被阻塞的。

從內部來說,當 MultithreadedTC 庫 執行測試,首先它執行 方法。然後,它建立每個用thread作為關鍵詞開頭的方法的執行緒(例子中是,方法 thread1(), thread2(), 和 thread3()),當全部執行緒都結束執行後,就執行 finish() 方法。為了執行測試,你要使用 TestFramework 類的 runOnce() 方法。

更多…

如果 MultithreadedTC library 檢測的全部執行緒都被阻塞,但是沒有一個是被 waitForTick() 方法阻塞的,那麼測試就會被認為在deadlock狀態,並丟擲 java.lang.IllegalStateException 異常。

參見

第八章,測試併發應用:FindBugs分析併發程式碼