線程 run() 無返回值,如何將子線程計算結果傳回主線程?本文對比四種常見模式,由簡入深。
// 方式一:繼承 Thread
class AddThread extends Thread {
public void run() { /* 計算 1+...+10000 */ }
}
// 方式二:實現 Runnable
class Add implements Runnable {
public void run() { /* 同樣計算 */ }
}
// 啟動:new Thread(new Add()).start();
class AddThread extends Thread {
private int result = 0;
public void run() { for(int i=1;i<=10000;i++) result+=i; }
public int getResult() { return result; }
}
public class Test {
public static void main(String[] args) {
AddThread t = new AddThread();
t.start();
System.out.println("result: " + t.getResult()); // 輸出 0!
}
}
原因:主線程與子線程並發,getResult() 在計算完成前調用。不可取。
class AddThread extends Thread {
private int result = 0;
private boolean done = false;
public void run() { for(int i=1;i<=10000;i++) result+=i; done=true; }
public boolean isDone() { return done; }
public int getResult() { return result; }
}
public class Test {
public static void main(String[] args) {
AddThread t = new AddThread(); t.start();
while (!t.isDone()) { try { Thread.sleep(100); } catch(Exception e){} }
System.out.println("result: " + t.getResult()); // 正確
}
}
缺點:主線程忙等消耗 CPU,sleep 間隔導致響應延遲。
class AddThread extends Thread {
private final Object lock;
private int result = 0;
private boolean done = false;
public AddThread(Object lock) { this.lock = lock; }
public void run() {
for(int i=1;i<=10000;i++) result+=i;
synchronized(lock) { done=true; lock.notifyAll(); }
}
public boolean isDone() { return done; }
public int getResult() { return result; }
}
public class Test {
public static void main(String[] args) {
Object lock = new Object();
AddThread t = new AddThread(lock); t.start();
synchronized(lock) {
while (!t.isDone()) { try { lock.wait(); } catch(Exception e){} }
}
System.out.println("result: " + t.getResult());
}
}
優點:主線程阻塞不佔用 CPU,響應即時。注意:wait/notify 必須在同步塊中,用 while 防虛假喚醒。
interface ResultProcessor { void process(int result); }
class AddThread extends Thread {
private final ResultProcessor processor;
public AddThread(ResultProcessor p) { this.processor = p; }
public void run() {
int result = 0; for(int i=1;i<=10000;i++) result+=i;
processor.process(result); // 計算完成直接回調
}
}
public class Test implements ResultProcessor {
public void process(int result) { System.out.println("result: " + result); }
public static void main(String[] args) {
new AddThread(new Test()).start(); // 無需主線程等待
}
}
優勢:解耦計算與處理、天然異步、支持多監聽者擴展、無同步煩惱。現代框架(如 CompletableFuture、ExecutorService)均採用類似回調/Future 模式。
| 模式 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 直接 get | 簡單 | 結果錯誤 | 不可用 |
| 輪詢 | 易理解 | 浪費 CPU、延遲 | 教學演示 |
| wait/notify | 阻塞高效、即時 | 代碼較繁瑣 | 底層協作、學習原理 |
| 回調 | 解耦、異步、擴展性強 | 需定義接口 | 生產環境首選 |