java 中的线程池原理

95 阅读3分钟

Java 线程池是一种管理并发线程的机制,通过复用线程减少创建和销毁线程的开销,从而提高应用程序的性能。线程池的主要原理包括以下几个方面:

1. 线程池的基本结构

  • 线程池(ThreadPoolExecutor)java.util.concurrent 包中的核心类。它主要由以下几个部分组成:
    • 任务队列(BlockingQueue workQueue): 保存等待执行的任务。常见的队列类型有 LinkedBlockingQueueSynchronousQueueArrayBlockingQueue 等。
    • 核心线程池大小(corePoolSize): 在没有超时的情况下,线程池中保持的最小线程数。
    • 最大线程池大小(maximumPoolSize): 线程池中能容纳的最大线程数。
    • 线程工厂(ThreadFactory): 用于创建新线程。
    • 拒绝策略(RejectedExecutionHandler): 当任务无法被执行时,线程池会调用拒绝策略处理这些任务。常见策略有 AbortPolicyDiscardPolicyDiscardOldestPolicyCallerRunsPolicy

2. 线程池的工作流程

  • 提交任务: 当任务被提交到线程池时,线程池根据当前的线程数量和任务队列的情况决定如何处理任务。
  • 执行任务:
    1. 如果当前线程数小于 corePoolSize,线程池会创建一个新线程来处理任务。
    2. 如果当前线程数大于等于 corePoolSize 且任务队列未满,任务会被放入任务队列中等待执行。
    3. 如果任务队列已满并且当前线程数小于 maximumPoolSize,线程池会创建新的线程来处理任务。
    4. 如果线程数达到 maximumPoolSize,且任务队列也满了,线程池会根据配置的拒绝策略来处理该任务。

3. 线程池的生命周期

  • Running: 线程池初始化完成,能接受新任务,并能处理已加入队列的任务。
  • Shutdown: 调用 shutdown() 方法后,不再接受新任务,但会处理已经在队列中的任务。
  • Stop: 调用 shutdownNow() 方法后,线程池不再接受新任务,并尝试终止正在执行的任务。
  • Terminated: 当所有任务执行完毕,且线程池中所有线程均被终止后,线程池会进入此状态。

4. 拒绝策略

  • 当线程池达到 maximumPoolSize 且队列已满时,线程池无法处理新的任务,便会触发拒绝策略。Java 提供了四种默认的拒绝策略:
    • AbortPolicy: 抛出 RejectedExecutionException 异常。
    • DiscardPolicy: 丢弃无法执行的任务,不做任何处理。
    • DiscardOldestPolicy: 丢弃队列最前面的任务,并尝试重新提交当前任务。
    • CallerRunsPolicy: 由调用线程处理该任务(即提交任务的线程)。

5. 线程池的优势

  • 减少资源消耗: 通过复用线程,避免了频繁创建和销毁线程所带来的性能开销。
  • 提高响应速度: 当任务到达时,线程池中已有线程可以立即执行任务,降低了延迟。
  • 统一管理: 线程池可以统一管理和监控线程的使用情况,避免系统资源耗尽。

6. 示例代码

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(
            2, // corePoolSize
            4, // maximumPoolSize
            60L, TimeUnit.SECONDS, // idle thread keep-alive time
            new LinkedBlockingQueue<>(100), // workQueue
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy() // rejection policy
        );

        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " is executing");
            });
        }

        executorService.shutdown();
    }
}

在这个例子中,线程池创建了一个核心池大小为 2,最大池大小为 4 的线程池,并配置了任务队列和拒绝策略。

7. 常见的线程池类型

  • FixedThreadPool: 固定大小的线程池,适用于需要限制线程数量的场景。
  • CachedThreadPool: 会根据需要创建新线程的线程池,适用于大量短期异步任务的场景。
  • SingleThreadExecutor: 只有一个线程的线程池,确保所有任务按照顺序执行。
  • ScheduledThreadPool: 用于执行定时任务或周期性任务的线程池。

通过理解线程池的工作原理,可以更好地管理并发任务,提高系统性能。