ThreadGroup 是 Java 早期提供的執行緒分組機制,允許將一組執行緒視為單一單位進行批次管理(如統一中斷、列舉存活執行緒)。然而,自 Java 5 起引入 ExecutorService 等高階併發工具後,官方文件與《Effective Java》均不再建議直接使用 ThreadGroup。本文透過完整範例展示其基本用法,並說明現代替代方案。
public class SearchResult {
private String winnerThreadName;
public String getWinnerThreadName() { return winnerThreadName; }
public void setWinnerThreadName(String name) { this.winnerThreadName = name; }
}
import java.util.Random;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class SearchTask implements Runnable {
private final SearchResult result;
public SearchTask(SearchResult result) { this.result = result; }
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.printf("Thread %s: Start%n", name);
try {
doSearch();
result.setWinnerThreadName(name);
} catch (InterruptedException e) {
System.out.printf("Thread %s: Interrupted%n", name);
return;
}
System.out.printf("Thread %s: End%n", name);
}
private void doSearch() throws InterruptedException {
Random random = new Random(new Date().getTime());
int waitSeconds = (int) (random.nextDouble() * 100);
System.out.printf("Thread %s: will sleep %d seconds%n", Thread.currentThread().getName(), waitSeconds);
TimeUnit.SECONDS.sleep(waitSeconds);
}
}
import java.util.concurrent.TimeUnit;
public class ThreadGroupDemo {
public static void main(String[] args) {
// 1. 建立執行緒群組
ThreadGroup group = new ThreadGroup("Searchers");
// 2. 共享結果物件與任務實例
SearchResult result = new SearchResult();
SearchTask task = new SearchTask(result);
// 3. 啟動 5 條執行緒,隸屬於群組
for (int i = 0; i < 5; i++) {
Thread t = new Thread(group, task, "Searcher-" + i);
t.start();
sleepQuietly(1, TimeUnit.SECONDS); // 錯峰啟動
}
// 4. 列印群組資訊
System.out.printf("Active threads: %d%n", group.activeCount());
System.out.println("Group info:");
group.list(); // 列印群組與執行緒詳細資訊
// 5. 列舉存活執行緒狀態
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads);
for (Thread t : threads) {
System.out.printf("Thread %s: %s%n", t.getName(), t.getState());
}
// 6. 等待首個任務完成(activeCount 降至 4 以下表示已有一條結束)
waitForFirstFinish(group);
// 7. 批次中斷其餘執行緒
System.out.println("Interrupting remaining threads...");
group.interrupt();
}
private static void waitForFirstFinish(ThreadGroup group) {
while (group.activeCount() > 4) { // 5 條啟動,完成一條剩 4
sleepQuietly(1, TimeUnit.SECONDS);
}
}
private static void sleepQuietly(long timeout, TimeUnit unit) {
try { unit.sleep(timeout); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); }
}
}
Thread Searcher-0: Start
Thread Searcher-0: will sleep 52 seconds
Thread Searcher-1: Start
Thread Searcher-1: will sleep 41 seconds
Thread Searcher-2: Start
Thread Searcher-2: will sleep 69 seconds
Thread Searcher-3: Start
Thread Searcher-3: will sleep 60 seconds
Thread Searcher-4: Start
Thread Searcher-4: will sleep 88 seconds
Active threads: 5
Group info:
java.lang.ThreadGroup[name=Searchers,maxpri=10]
Thread[Searcher-0,5,Searchers]
Thread[Searcher-1,5,Searchers]
Thread[Searcher-2,5,Searchers]
Thread[Searcher-3,5,Searchers]
Thread[Searcher-4,5,Searchers]
Thread Searcher-0: TIMED_WAITING
Thread Searcher-1: TIMED_WAITING
Thread Searcher-2: TIMED_WAITING
Thread Searcher-3: TIMED_WAITING
Thread Searcher-4: TIMED_WAITING
Thread Searcher-1: Interrupted
Thread Searcher-4: Interrupted
Thread Searcher-2: Interrupted
Thread Searcher-0: Interrupted
Thread Searcher-3: Interrupted
| 方法 | 說明 |
|---|---|
activeCount() | 估算群組中存活執行緒數(含子群組) |
enumerate(Thread[]) | 將存活執行緒複製到陣列 |
interrupt() | 中斷群組中所有執行緒 |
list() | 列印群組結構到標準輸出(除錯用) |
getParent() | 取得父群組 |
uncaughtException(Thread, Throwable) | 執行緒因未捕獲異常終止時的回調,可覆寫實作統一異常處理 |
activeCount() 回傳估算值而非精確值;enumerate() 若陣列太小會靜默截斷,導致遺漏執行緒。uncaughtException 機制易被忽略,且無法針對特定任務定制恢復策略。ExecutorService、CompletableFuture、ThreadPoolExecutor 提供更豐富、安全、高效的抽象。import java.util.concurrent.*;
public class ExecutorDemo {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService cs = new ExecutorCompletionService<>(executor);
// 提交 5 個任務
for (int i = 0; i < 5; i++) {
final int id = i;
cs.submit(() -> {
Thread.sleep(new Random().nextInt(100) * 1000L);
return "Searcher-" + id;
});
}
// 取得最快完成的任務
String winner = cs.take().get(); // 阻塞直到有任務完成
System.out.println("Winner: " + winner);
// 取消其餘任務
executor.shutdownNow();
System.out.println("Remaining tasks cancelled.");
}
}
優勢:精確控制任務生命週期、自動管理執行緒池、支援 Future 獲取結果/異常、程式碼更簡潔易讀。
理解 ThreadGroup 有助於閱讀舊版程式碼與 JVM 內部機制,但在新專案中應優先採用 ExecutorService 等高階併發工具。若確有需求對執行緒群組進行結構化管理(如自定執行緒工廠、監控),可考慮實作 ThreadFactory 搭配 ThreadGroup 僅作為命名空間,核心調度仍交給 Executor 體系。