第8章

🏊‍♂️ 线程池基础

深入理解线程池的概念、工作原理和核心组件,掌握不同类型线程池的特性

学习目标

线程池的概念

线程池(Thread Pool)是一种多线程处理形式,它预先创建若干个线程,这些线程在没有任务处理时处于等待状态,当有任务来临时就分配给其中的一个线程来处理,任务处理完成后线程并不会被销毁,而是继续等待下一个任务。

核心理解

线程池的本质是对线程资源的复用和管理,通过预创建线程、任务队列和调度机制来提高系统性能和资源利用率。

为什么需要线程池

资源复用
避免频繁创建和销毁线程的开销,提高系统性能。
响应速度
任务到达时可以立即执行,无需等待线程创建。
可管理性
统一管理线程,便于监控、调优和资源分配。

直接创建线程 vs 使用线程池

// 直接创建线程的方式
for (int i = 0; i < 1000; i++) {
    new Thread(() -> {
        // 执行任务
        doSomeWork();
    }).start();
}

// 使用线程池的方式
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
    executor.submit(() -> {
        // 执行任务
        doSomeWork();
    });
}
executor.shutdown();

线程池的优势

线程池相比直接创建线程具有显著的优势,这些优势使得线程池成为并发编程中的重要工具。

性能优势

管理优势

管理特性
  • 资源控制:限制并发线程数量,防止资源耗尽
  • 任务调度:支持延迟执行、定时执行等调度策略
  • 状态监控:提供线程池状态和任务执行情况的监控
  • 异常处理:统一的异常处理机制
  • 优雅关闭:支持优雅的关闭和资源清理

线程池架构

理解线程池的内部架构是掌握线程池技术的关键。线程池主要由核心组件、工作流程和任务队列三部分组成。

核心组件

工作线程
  • 核心线程:始终保持活跃的线程
  • 非核心线程:根据需要创建的临时线程
  • 线程工厂:负责创建新线程
任务队列
  • 阻塞队列:存储待执行的任务
  • 队列类型:有界队列、无界队列
  • 队列策略:FIFO、LIFO、优先级
拒绝策略
  • AbortPolicy:抛出异常
  • CallerRunsPolicy:调用者执行
  • DiscardPolicy:丢弃任务
  • DiscardOldestPolicy:丢弃最老任务

工作流程

// ThreadPoolExecutor的核心参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,    // 核心线程数
    maximumPoolSize, // 最大线程数
    keepAliveTime,   // 线程空闲时间
    TimeUnit.SECONDS,// 时间单位
    workQueue,       // 任务队列
    threadFactory,   // 线程工厂
    rejectedHandler  // 拒绝策略
);
执行流程
  1. 任务提交到线程池
  2. 如果核心线程未满,创建新的核心线程执行任务
  3. 如果核心线程已满,任务加入队列等待
  4. 如果队列已满且未达到最大线程数,创建非核心线程
  5. 如果达到最大线程数,执行拒绝策略

线程池类型

Java提供了几种预定义的线程池类型,每种类型都有其特定的使用场景和特性。

常用线程池类型

FixedThreadPool

特点:固定线程数量的线程池

适用场景:负载比较重的服务器

ExecutorService executor = 
    Executors.newFixedThreadPool(5);
CachedThreadPool

特点:可缓存的线程池,线程数可动态调整

适用场景:执行很多短期异步任务

ExecutorService executor = 
    Executors.newCachedThreadPool();
SingleThreadExecutor

特点:只有一个线程的线程池

适用场景:需要保证顺序执行的任务

ExecutorService executor = 
    Executors.newSingleThreadExecutor();

线程池对比

选择建议
  • CPU密集型任务:线程数 = CPU核心数 + 1
  • IO密集型任务:线程数 = CPU核心数 × 2
  • 混合型任务:根据实际情况调优
  • 建议:使用ThreadPoolExecutor自定义线程池参数

生命周期管理

正确管理线程池的生命周期对于应用程序的稳定性和资源利用率至关重要。

线程池状态

RUNNING
接受新任务并处理队列中的任务
SHUTDOWN
不接受新任务,但会处理队列中的任务
STOP
不接受新任务,不处理队列任务,中断正在执行的任务

优雅关闭

// 优雅关闭线程池
executor.shutdown(); // 启动有序关闭

try {
    // 等待已提交任务完成
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        executor.shutdownNow(); // 强制关闭
        
        // 等待任务响应中断
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            System.err.println("线程池未能正常关闭");
        }
    }
} catch (InterruptedException ie) {
    executor.shutdownNow();
    Thread.currentThread().interrupt();
}
注意事项
  • 及时关闭:应用程序结束时必须关闭线程池
  • 优雅关闭:优先使用shutdown()而不是shutdownNow()
  • 超时处理:设置合理的等待超时时间
  • 异常处理:处理关闭过程中可能出现的异常
上一章:线程通信 返回目录 下一章:ThreadPoolExecutor详解