首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 併發程式設計實戰(十):執行緒群組 ThreadGroup 應用與替代方案

     編輯:Java 相關知識

前言

ThreadGroup 是 Java 早期提供的執行緒分組機制,允許將一組執行緒視為單一單位進行批次管理(如統一中斷、列舉存活執行緒)。然而,自 Java 5 起引入 ExecutorService 等高階併發工具後,官方文件與《Effective Java》均不再建議直接使用 ThreadGroup。本文透過完整範例展示其基本用法,並說明現代替代方案。

ThreadGroup 基本範例

場景:模擬搜尋任務,首個完成即取消其餘執行緒

1. 結果容器

public class SearchResult {
    private String winnerThreadName;

    public String getWinnerThreadName() { return winnerThreadName; }
    public void setWinnerThreadName(String name) { this.winnerThreadName = name; }
}

2. 搜尋任務

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);
    }
}

3. 主程式:建立群組、啟動執行緒、監控與中斷

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

ThreadGroup 主要方法速查

方法說明
activeCount()估算群組中存活執行緒數(含子群組)
enumerate(Thread[])將存活執行緒複製到陣列
interrupt()中斷群組中所有執行緒
list()列印群組結構到標準輸出(除錯用)
getParent()取得父群組
uncaughtException(Thread, Throwable)執行緒因未捕獲異常終止時的回調,可覆寫實作統一異常處理

為何不建議使用 ThreadGroup?

  1. API 設計缺陷:activeCount() 回傳估算值而非精確值;enumerate() 若陣列太小會靜默截斷,導致遺漏執行緒。
  2. 功能單薄:無法支援任務提交、結果獲取、排程、執行緒池複用等核心併發需求。
  3. 錯誤處理困難:uncaughtException 機制易被忽略,且無法針對特定任務定制恢復策略。
  4. 現代替代方案完善:ExecutorService、CompletableFuture、ThreadPoolExecutor 提供更豐富、安全、高效的抽象。

現代替代方案:ExecutorService 實作同樣場景

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 體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved