隨著多核 CPU 普及,並行程式設計成為常態。然而,多執行緒錯誤(資料競爭、死結)具有極強的隨機性與隱蔽性:程式可能正確執行上千次,下一次卻突然失敗。傳統測試難以覆蓋所有執行緒交錯路徑。IBM 開發的 MTRAT (Multi-Thread Run-time Analysis Tool) 專為解決此問題而生,能在執行期動態偵測潛在的並行錯誤。
MTRAT 整合多項技術於單一工具,降低使用複雜度:
當兩個執行緒在無同步保護下存取同一共享變數,且至少有一個為寫入操作時,即發生資料競爭。結果取決於執行緒排程順序,極難重現。
package sample;
class Value {
private int x;
public Value() { x = 0; }
public synchronized void add(Value v) { x = x + v.get(); }
public int get() { return x; }
}
class Task extends Thread {
Value v1, v2;
public Task(Value v1, Value v2) { this.v1 = v1; this.v2 = v2; }
public void run() { v1.add(v2); }
}
public class DataRace {
public static void main(String[] args) throws InterruptedException {
Value v1 = new Value(), v2 = new Value();
Thread t1 = new Task(v1, v2);
Thread t2 = new Task(v2, v1); // 交叉傳遞,造成雙向存取
t1.start(); t2.start();
t1.join(); t2.join();
}
}
問題分析: add 方法雖同步,但鎖住的是 v1;get() 卻無同步。執行緒 1 鎖 v1 呼叫 v2.get(),執行緒 2 鎖 v2 呼叫 v1.get(),雙方同時讀寫對方的 x 欄位,發生競爭。
$ mtrat -cp . sample.DataRace
Data Race 1 : sample/Value : x
Thread Thread-3 : WRITE (v1.add -> v2.get)
Thread Thread-4 : READ (v2.add -> v1.get)
Data Race 2 : sample/Value : x
Thread Thread-3 : READ (v1.add -> v2.get)
Thread Thread-4 : WRITE (v2.add -> v1.get)
MTRAT 精確指出:類別 Value 欄位 x 存在兩個雙向競爭,並給出完整呼叫堆疊。

圖形介面顯示競爭雙方的執行緒、方法堆疊與原始碼行號,方便快速定位。
class T3 extends Thread {
StringBuffer L1, L2;
public T3(StringBuffer L1, StringBuffer L2) { this.L1 = L1; this.L2 = L2; }
public void run() {
synchronized (L1) { synchronized (L2) { } } // 依序取得 L1, L2
}
}
public class Deadlock {
void harness2() throws InterruptedException {
StringBuffer L1 = new StringBuffer("L1"), L2 = new StringBuffer("L2");
Thread t1 = new T3(L1, L2); // 先鎖 L1 再鎖 L2
Thread t2 = new T3(L2, L1); // 先鎖 L2 再鎖 L1 -> 死結
t1.start(); t2.start(); t1.join(); t2.join();
}
public static void main(String[] args) throws InterruptedException {
new Deadlock().harness2();
}
}
$ mtrat -Dcom.ibm.mtrat.deadlock=true -cp . sample.Deadlock
Thread 7 : Acquire L1 L2
Dead Lock 1
Thread 7, acquired lock1 -> try lock2 sample/T3 line 109
Thread 8, acquired lock2 -> try lock1 sample/T3 line 109
Thread 8 : Acquire L2 L1
報告清晰顯示:執行緒 7 持有 L1 等待 L2,執行緒 8 持有 L2 等待 L1,形成循環等待。

void harness2() throws InterruptedException {
StringBuffer L1 = new StringBuffer("L1"), L2 = new StringBuffer("L2");
Thread t1 = new T3(L1, L2);
Thread t2 = new T3(L1, L2); // 兩者相同順序取得鎖
t1.start(); t2.start(); t1.join(); t2.join();
}
MTRAT 以動態位元碼插桿與線上分析技術,解決了並行錯誤「難重現、難定位」的痛點。對於開發高可靠度多執行緒 Java 應用的團隊,它是不可或缺的品質保障工具。立即從 IBM alphaWorks 下載試用吧!