iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
自我挑戰組

我的Java自學之路:一個轉職者的30篇技術統整系列 第 13

Java進階:執行緒池與執行器框架

  • 分享至 

  • xImage
  •  

1. 執行緒池的概念與優勢

1.1 什麼是執行緒池

執行緒池是一種執行緒使用模式。它是一種預先創建並維護多個執行緒的技術,這些執行緒可以用來執行多個任務。當有新任務需要執行時,會從池中取出一個空閒的執行緒來執行任務,任務執行完畢後,該執行緒會被放回池中等待下一個任務。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                System.out.println("執行任務: " + Thread.currentThread().getName());
            });
        }
        
        executor.shutdown();
    }
}

1.2 為什麼使用執行緒池

使用執行緒池主要有以下幾個原因:

  1. 重用執行緒:避免頻繁創建和銷毀執行緒,減少系統開銷。
  2. 控制並發數:限制系統中執行緒的數量,防止資源耗盡。
  3. 提高響應速度:任務到達時,無需等待執行緒創建即可立即執行。
  4. 提供管理功能:提供定時執行、定期執行、並發數控制等管理功能。

1.3 執行緒池的優勢

執行緒池相較於傳統的執行緒使用方式有以下優勢:

  1. 性能提升
    • 減少執行緒創建和銷毀的開銷
    • 提高系統響應速度
// 使用執行緒池
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 100; i++) {
    executor.execute(new Task());
}

// 不使用執行緒池
for (int i = 0; i < 100; i++) {
    new Thread(new Task()).start();
}
  1. 資源管理

    • 有效控制系統資源的使用
    • 防止資源耗盡
  2. 提高可管理性

    • 提供豐富的管理接口
    • 支持執行緒的生命週期管理
  3. 提供更多功能

    • 支持定時執行
    • 支持異步處理(如Future)
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "任務完成";
});
System.out.println(future.get()); // 等待並獲取結果
  1. 隔離性
    • 將任務提交與任務執行解耦
    • 提高系統的模組化程度

通過使用執行緒池,我們可以更有效地管理並發任務,提高應用程式的性能和可靠性。

2. Java中的Executor框架

Java的Executor框架是一個強大的執行緒池實現,定義在java.util.concurrent包中,主要包含以下幾個核心介面和類。

2.1 Executor介面

Executor是最基本的執行器介面,只定義了一個方法:

public interface Executor {
    void execute(Runnable command);
}

這個介面將任務的提交與執行解耦,使得我們可以輕鬆地改變任務執行的方式而不影響任務的提交。

使用示例:

Executor executor = Executors.newSingleThreadExecutor();
executor.execute(() -> System.out.println("Hello from Executor!"));

2.2 ExecutorService介面

ExecutorService擴展了Executor介面,提供了更多的功能,包括管理執行器的生命週期和異步任務的處理。

主要方法包括:

  • submit():提交一個Callable或Runnable任務並返回Future
  • invokeAll():執行給定的任務集合,返回一個Future列表
  • invokeAny():執行給定的任務集合,返回其中一個成功完成的結果
  • shutdown():關閉執行器,但允許已提交的任務繼續執行
  • shutdownNow():立即關閉執行器,停止所有正在執行的任務

使用示例:

ExecutorService service = Executors.newFixedThreadPool(2);
Future<String> future = service.submit(() -> "任務結果");
System.out.println(future.get());
service.shutdown();

2.3 Executors工廠類

Executors是一個工廠類,提供了創建各種預定義執行器的靜態方法。

主要方法包括:

  • newFixedThreadPool(int nThreads):創建固定大小的執行緒池
  • newCachedThreadPool():創建一個可根據需要創建新執行緒的執行緒池
  • newSingleThreadExecutor():創建只有一個執行緒的執行器
  • newScheduledThreadPool(int corePoolSize):創建一個可以執行定時任務的執行緒池

使用示例:

ExecutorService fixedPool = Executors.newFixedThreadPool(5);
ExecutorService cachedPool = Executors.newCachedThreadPool();
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// 使用固定大小的執行緒池
for (int i = 0; i < 10; i++) {
    final int taskId = i;
    fixedPool.execute(() -> System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName()));
}

// 使用可調度的執行緒池
scheduledPool.scheduleAtFixedRate(() -> System.out.println("Scheduled task"), 0, 1, TimeUnit.SECONDS);

// 記得在使用完畢後關閉執行器
fixedPool.shutdown();
cachedPool.shutdown();
singleThreadExecutor.shutdown();
scheduledPool.shutdown();

3. 常用的執行器服務

Java的Executor框架提供幾種預定義的執行器服務,每種都有其特定的用途和特性。

3.1 FixedThreadPool

FixedThreadPool是一個固定大小的執行緒池,特點是:

  • 核心執行緒數和最大執行緒數相同
  • 執行緒空閒時不會被回收
  • 使用無界隊列來存儲待執行的任務

使用示例:

ExecutorService fixedPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
    final int taskId = i;
    fixedPool.execute(() -> {
        System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());
    });
}
fixedPool.shutdown();

適用場景:

  • 需要限制並發執行緒數量的場景
  • 負載較重的服務器

3.2 CachedThreadPool

CachedThreadPool是一個可根據需要創建新執行緒的執行緒池。它的特點是:

  • 核心執行緒數為0,最大執行緒數為Integer.MAX_VALUE
  • 執行緒空閒60秒後會被回收
  • 使用SynchronousQueue來存儲待執行的任務

使用示例:

ExecutorService cachedPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
    final int taskId = i;
    cachedPool.execute(() -> {
        System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());
    });
}
cachedPool.shutdown();

適用場景:

  • 執行大量短期異步任務
  • 負載較輕的服務器

3.3 SingleThreadExecutor

SingleThreadExecutor是只有一個執行緒的執行器,特點是:

  • 核心執行緒數和最大執行緒數都為1
  • 使用無界隊列來存儲待執行的任務
  • 保證任務按照提交的順序執行

使用示例:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
    final int taskId = i;
    singleThreadExecutor.execute(() -> {
        System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());
    });
}
singleThreadExecutor.shutdown();

適用場景:

  • 需要保證任務順序執行的場景
  • 不需要利用多核優勢的場景

3.4 ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是一個可以執行定時任務的執行緒池,特點是:

  • 可以設置核心執行緒數,最大執行緒數為Integer.MAX_VALUE
  • 可以執行延遲任務和週期性任務

使用示例:

ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// 延遲3秒後執行
scheduledPool.schedule(() -> System.out.println("Delayed task"), 3, TimeUnit.SECONDS);

// 延遲1秒後開始執行,每2秒執行一次
scheduledPool.scheduleAtFixedRate(() -> System.out.println("Periodic task"), 1, 2, TimeUnit.SECONDS);

// 讓程式運行一段時間
Thread.sleep(10000);

scheduledPool.shutdown();

適用場景:

  • 需要執行定時任務或週期性任務的場景
  • 需要多個執行緒執行定時任務的場景

在選擇使用哪種執行器服務時,需要根據具體的應用場景和需求來決定。例如,如果需要限制並發執行緒數量,可以使用FixedThreadPool;如果需要執行大量短期異步任務,可以使用CachedThreadPool;如果需要保證任務順序執行,可以使用SingleThreadExecutor;如果需要執行定時任務,可以使用ScheduledThreadPoolExecutor。

在下一章節中,我們將探討如何向這些執行器提交任務,以及如何處理任務的執行結果。

4. 提交任務到執行器

在使用執行器服務時,我們需要知道如何正確地提交任務並處理執行結果。本章節將介紹幾種常用的任務提交方法。

4.1 execute()方法

execute()方法是最基本的任務提交方法,它來自Executor介面。這個方法接受一個Runnable對象,並且沒有返回值。

使用示例:

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
    System.out.println("Task executed by " + Thread.currentThread().getName());
});
executor.shutdown();

特點:

  • 簡單易用
  • 無法獲取任務執行結果
  • 無法知道任務是否執行成功

4.2 submit()方法

submit()方法是ExecutorService介面提供的方法,它可以提交Runnable或Callable任務,並返回一個Future對象。

使用示例:

ExecutorService executor = Executors.newFixedThreadPool(2);

// 提交Runnable任務
Future<?> future1 = executor.submit(() -> {
    System.out.println("Runnable task executed");
});

// 提交Callable任務
Future<String> future2 = executor.submit(() -> {
    return "Callable task result";
});

System.out.println(future2.get()); // 輸出: Callable task result

executor.shutdown();

特點:

  • 可以獲取任務執行結果
  • 可以取消任務
  • 可以檢查任務是否完成

4.3 invokeAll()和invokeAny()方法

這兩個方法允許同時提交多個任務。

invokeAll()執行所有任務,並返回所有任務的Future列表:

ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = Arrays.asList(
    () -> "Task 1 result",
    () -> "Task 2 result",
    () -> "Task 3 result"
);

List<Future<String>> futures = executor.invokeAll(tasks);
for (Future<String> future : futures) {
    System.out.println(future.get());
}

executor.shutdown();

invokeAny()執行所有任務,但只返回其中一個成功完成的任務的結果:

ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = Arrays.asList(
    () -> {
        Thread.sleep(2000);
        return "Task 1 result";
    },
    () -> "Task 2 result",
    () -> {
        Thread.sleep(1000);
        return "Task 3 result";
    }
);

String result = executor.invokeAny(tasks);
System.out.println(result); // 可能輸出: Task 2 result

executor.shutdown();

特點:

  • invokeAll()適用於需要執行所有任務並獲取所有結果的場景
  • invokeAny()適用於只需要一個成功結果的場景,如並行搜索

4.4 處理執行結果和異常

當使用submit()或invokeAll()方法時,我們得到的是Future對象,通過以下方式處理結果和異常:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
    if (Math.random() < 0.5) {
        throw new Exception("Task failed");
    }
    return "Task succeeded";
});

try {
    String result = future.get();
    System.out.println(result);
} catch (InterruptedException e) {
    System.out.println("Task was interrupted");
} catch (ExecutionException e) {
    System.out.println("Task threw an exception: " + e.getCause().getMessage());
}

executor.shutdown();

注意事項:

  1. Future.get()方法會阻塞直到任務完成,可以使用帶超時參數的get()方法來避免無限等待。
  2. 任務中拋出的異常會被包裝在ExecutionException中。
  3. 如果任務被取消,future.get()會拋出CancellationException。

通過正確地提交任務和處理結果,我們可以充分利用執行器服務的功能,實現高效的並發程式設計。在下一章節中,我們將探討如何管理執行器的生命週期。

5. 管理執行器生命週期

正確管理執行器的生命週期對於應用程式的穩定性和資源管理至關重要。

5.1 啟動執行器

執行器在創建時就已經啟動,無需額外的啟動步驟。例如:

ExecutorService executor = Executors.newFixedThreadPool(5);
// 執行器已經準備好接受任務

5.2 關閉執行器

ExecutorService提供兩種關閉執行器的方法:

  1. shutdown():
    • 停止接受新任務
    • 等待已提交的任務執行完成
    • 不會中斷正在執行的任務
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    // 執行一些任務
});
executor.shutdown();
  1. shutdownNow():
    • 停止接受新任務
    • 嘗試停止所有正在執行的任務
    • 返回等待執行的任務列表
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    // 執行一些任務
});
List<Runnable> notExecutedTasks = executor.shutdownNow();

5.3 等待任務完成

在關閉執行器後,我們可能需要等待所有任務完成。
ExecutorService提供了幾種方法來實現這一點:

  1. awaitTermination():
    • 阻塞等待執行器終止,或超時
    • 返回布林值表示是否所有任務都已完成
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    // 執行一些長時間運行的任務
});
executor.shutdown();
try {
    if (executor.awaitTermination(60, TimeUnit.SECONDS)) {
        System.out.println("執行器已終止");
    } else {
        System.out.println("執行器在超時前未終止");
    }
} catch (InterruptedException e) {
    System.out.println("等待被中斷");
}
  1. isTerminated():
    • 檢查執行器是否已終止
    • 只有在shutdown()或shutdownNow()被調用後才可能返回true
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.shutdown();
while (!executor.isTerminated()) {
    // 等待所有任務完成
}
System.out.println("所有任務已完成");

5.4 關閉執行器

void shutdownAndAwaitTermination(ExecutorService pool) {
    pool.shutdown(); // 禁止新任務提交
    try {
        // 等待現有任務完成
        if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
            pool.shutdownNow(); // 取消當前執行的任務
            // 等待任務響應中斷
            if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                System.err.println("執行器未能終止");
        }
    } catch (InterruptedException ie) {
        // (重新)取消如果當前執行緒也被中斷
        pool.shutdownNow();
        // 保留中斷狀態
        Thread.currentThread().interrupt();
    }
}

5.5 處理未完成的任務

在某些情況下,我們可能需要處理那些在執行器關閉時尚未完成的任務:

ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交一些任務...

List<Runnable> notExecutedTasks = executor.shutdownNow();
for (Runnable task : notExecutedTasks) {
    // 處理未執行的任務,例如重新安排或記錄
    System.out.println("未執行的任務: " + task);
}

正確管理執行器的生命週期可以確保:

  1. 所有提交的任務都得到執行
  2. 系統資源得到及時釋放
  3. 應用程式可以正常關閉

6. 自定義執行緒池

雖然Java提供了幾種預定義的執行器服務,但在某些情況下,我們可能需要更細緻的控制來滿足特定的應用需求,我們可以使用ThreadPoolExecutor類來自定義執行緒池。

6.1 ThreadPoolExecutor類

ThreadPoolExecutor是Java執行緒池的核心實現類,允許我們根據具體需求調整執行緒池的行為。

基本構造函數:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

6.2 核心參數設置

  1. corePoolSize:核心執行緒數

    • 即使空閒,除非設置了allowCoreThreadTimeOut,否則這些執行緒也不會被終止
  2. maximumPoolSize:最大執行緒數

    • 執行緒池中允許的最大執行緒數
  3. keepAliveTime:執行緒空閒時間

    • 當執行緒數大於核心數時,這是多餘的空閒執行緒在終止前等待新任務的最長時間
  4. unit:keepAliveTime參數的時間單位

  5. workQueue:工作隊列

    • 用於保存任務的阻塞隊列
  6. threadFactory:執行緒工廠

    • 用於創建新執行緒
  7. handler:拒絕策略

    • 當執行緒池已滿且工作隊列也已滿時,如何處理新提交的任務

6.3 自定義執行緒池示例

以下是一個自定義執行緒池的示例:

import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        int corePoolSize = 2;
        int maximumPoolSize = 4;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        
        ThreadFactory threadFactory = new ThreadFactory() {
            private int count = 1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Custom-Thread-" + count++);
            }
        };
        
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

        // 提交任務
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 關閉執行器
        executor.shutdown();
    }
}

6.4 常用的工作隊列

  1. ArrayBlockingQueue:有界隊列,基於數組
  2. LinkedBlockingQueue:可選有界或無界隊列,基於鏈表
  3. SynchronousQueue:不存儲元素的阻塞隊列,每個插入操作必須等待另一個執行緒的移除操作
  4. PriorityBlockingQueue:無界優先級隊列

6.5 常用的拒絕策略

  1. AbortPolicy:拋出RejectedExecutionException異常 (默認策略)
  2. CallerRunsPolicy:在調用者的執行緒中直接執行任務
  3. DiscardPolicy:直接丟棄任務,不做任何處理
  4. DiscardOldestPolicy:丟棄隊列中最老的任務,然後重新嘗試執行當前任務

6.6 調整和監控

ThreadPoolExecutor提供了許多方法來動態調整和監控執行緒池:

// 動態調整核心執行緒數
executor.setCorePoolSize(newSize);

// 動態調整最大執行緒數
executor.setMaximumPoolSize(newSize);

// 獲取當前執行緒數
int threadCount = executor.getPoolSize();

// 獲取活躍執行緒數
int activeCount = executor.getActiveCount();

// 獲取已完成任務數
long completedTaskCount = executor.getCompletedTaskCount();

通過自定義執行緒池,我們可以更精確地控制執行緒的創建、銷毀以及任務的處理方式,從而優化應用程式的性能和資源使用。在實際應用中,應根據具體的負載特性和系統資源來調整這些參數,以達到最佳效果。

7. Fork/Join框架

Fork/Join框架是Java 7引入的一個用於並行執行任務的框架,是ExecutorService接口的一個實現。
這個框架被設計用來有效地使用多處理器系統,特別適合於使用分治法來解決問題。

7.1 分治算法

分治法是一種解決複雜問題的方法,它的基本思想是:

  1. 將一個複雜的問題分解(Fork)成多個相似的子問題
  2. 遞迴地解決這些子問題
  3. 最後將子問題的結果合併(Join)成原問題的解

7.2 ForkJoinPool

ForkJoinPool是Fork/Join框架的核心類,它實現了工作竊取算法(work-stealing algorithm)。

基本用法:

ForkJoinPool pool = new ForkJoinPool();
Result result = pool.invoke(new MyTask());

或者使用公共的ForkJoinPool:

Result result = ForkJoinPool.commonPool().invoke(new MyTask());

7.3 RecursiveTask和RecursiveAction

Fork/Join框架提供了兩個主要的任務類:

  1. RecursiveTask: 有返回值的任務
  2. RecursiveAction: 沒有返回值的任務

這兩個類都繼承自ForkJoinTask。

使用RecursiveTask的例子 - 計算斐波那契數列:

import java.util.concurrent.RecursiveTask;

public class FibonacciTask extends RecursiveTask<Integer> {
    final int n;

    FibonacciTask(int n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n <= 1)
            return n;
        FibonacciTask f1 = new FibonacciTask(n - 1);
        f1.fork();
        FibonacciTask f2 = new FibonacciTask(n - 2);
        return f2.compute() + f1.join();
    }

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        System.out.println(pool.invoke(new FibonacciTask(10)));
    }
}

7.4 關鍵方法

  1. fork(): 異步執行一個子任務
  2. join(): 等待子任務完成並獲取結果
  3. compute(): 定義任務的具體邏輯

7.5 使用Fork/Join框架的最佳實踐

  1. 對於小規模的任務,直接計算而不是繼續拆分
  2. ForkJoinTask應該只執行計算,避免I/O操作
  3. 避免使用同步方法,因為這可能導致執行緒閒置
  4. 任務不應該依賴其他任務的執行順序
  5. 利用Java 8的並行流(Parallel Streams),它們內部使用Fork/Join框架

使用並行流的例子:

import java.util.Arrays;

public class ParallelStreamExample {
    public static void main(String[] args) {
        long[] numbers = new long[1000000];
        Arrays.fill(numbers, 1);

        long sum = Arrays.stream(numbers).parallel().sum();
        System.out.println("Sum: " + sum);
    }
}

7.6 Fork/Join框架的優勢

  1. 自動利用多核處理器
  2. 使用工作竊取算法,提高執行效率
  3. 適合解決可以遞迴分解的大規模問題
  4. 與ExecutorService接口兼容,易於集成到現有代碼中

7.7 注意事項

  1. 不適合I/O密集型任務
  2. 對於小規模問題,可能不如普通的順序執行效率高
  3. 需要合理設置任務的粒度,過細的粒度可能導致過多的開銷

通過使用Fork/Join框架,我們可以更容易地實現複雜的並行計算,特別是對於那些可以被分解成小任務的大規模問題。

8. 實踐與注意事項

8.1 選擇合適的執行器

  1. 任務特性:

    • 短期且頻繁的任務: 使用CachedThreadPool
    • 長期運行的任務: 使用FixedThreadPool
    • 需要定時或週期性執行的任務: 使用ScheduledThreadPool
    • 單線程順序執行: 使用SingleThreadExecutor
  2. 系統資源:

    • 考慮系統的CPU核心數
    • 考慮可用的記憶體大小
    • 考慮I/O密集型還是CPU密集型任務
int processors = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(processors);

8.2 正確關閉執行器

  1. 使用shutdown()而不是shutdownNow(),除非你確實需要立即停止所有任務
  2. 使用awaitTermination()等待任務完成
  3. 在finally塊中關閉執行器,確保資源被釋放
ExecutorService executor = Executors.newFixedThreadPool(4);
try {
    // 執行任務
} finally {
    executor.shutdown();
    try {
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow();
        }
    } catch (InterruptedException e) {
        executor.shutdownNow();
    }
}

8.3 處理異常

  1. 使用try-catch塊在任務內部處理異常
  2. 使用UncaughtExceptionHandler處理未捕獲的異常
  3. 當使用submit()方法時,通過Future.get()來捕獲異常
executor.submit(() -> {
    try {
        // 任務邏輯
    } catch (Exception e) {
        // 處理異常
    }
});

8.4 避免資源耗盡

  1. 使用有界隊列來限制任務數量
  2. 設置合理的核心線程數和最大線程數
  3. 使用合適的拒絕策略
int corePoolSize = 2;
int maxPoolSize = 4;
long keepAliveTime = 60L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, handler);

8.5 避免線程洩漏

  1. 確保所有提交的任務最終都會完成
  2. 避免使用無限循環,除非有明確的終止條件
  3. 正確處理中斷
while (!Thread.currentThread().isInterrupted()) {
    // 任務邏輯
}

8.6 合理設置任務粒度

  1. 任務不要太小,避免頻繁的上下文切換
  2. 任務不要太大,以充分利用多核處理器
  3. 考慮使用Fork/Join框架來自動平衡任務粒度

8.7 監控和調優

  1. 使用JConsole或VisualVM等工具監控線程池狀態
  2. 記錄關鍵指標,如活躍線程數、完成任務數等
  3. 根據監控結果動態調整線程池參數
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
System.out.println("Active threads: " + executor.getActiveCount());
System.out.println("Completed tasks: " + executor.getCompletedTaskCount());

8.8 使用線程安全的數據結構

  1. 優先使用java.util.concurrent包中的線程安全集合
  2. 使用不可變對象來減少同步需求
  3. 必要時使用同步工具類,如CountDownLatch, CyclicBarrier等
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

遵循這些最佳實踐並注意潛在的問題,可以幫助我們更有效地使用Java執行緒池和執行器框架,提高應用程序的性能和可靠性。在實際應用中,應該根據具體情況進行調整和優化。

本篇文章同步刊載: JYI.TW
筆者個人的網站: JUNYI


上一篇
Java進階:執行緒安全與同步機制
下一篇
Java進階:並行集合與原子操作
系列文
我的Java自學之路:一個轉職者的30篇技術統整15
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言