深入浅出Java线程池

深入浅出Java线程池

什么是线程池?

想象你开了一家快递站,每天有很多包裹要派送。如果每来一个包裹就雇一个新快递员,送完就解雇,这样效率很低,因为:

  1. 频繁招聘和解雇成本高(对应线程创建和销毁开销大)
  2. 新快递员不熟悉路线(线程需要时间初始化)
  3. 快递员太多时管理混乱(系统资源耗尽)

线程池就像你预先雇佣的一批固定快递员(线程),有包裹(任务)来了就分配给他们,送完继续等待新任务,这样效率更高。

为什么需要线程池?

  1. 降低资源消耗:重复利用已创建的线程,避免频繁创建销毁
  2. 提高响应速度:任务到达时直接使用现有线程,无需等待线程创建
  3. 便于管理:可以统一分配、监控和调优线程资源

Java线程池核心类

Java中的线程池主要通过java.util.concurrent包中的ExecutorService接口及其实现类ThreadPoolExecutor来实现。

线程池工作原理

线程池就像一个有管理的"线程工厂+任务队列":

  1. 核心线程:池中常驻的基本劳动力,即使空闲也不销毁
  2. 任务队列:当核心线程都忙时,新任务进入队列等待
  3. 非核心线程:当队列满了,创建额外线程帮忙(有数量限制)
  4. 拒绝策略:当线程和队列都满了,如何处理新任务

创建线程池的常用方法

Java提供了Executors工厂类来创建常见类型的线程池:

java 复制代码
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);

// 2. 单线程池(保证任务顺序执行)
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

// 3. 可缓存线程池(自动扩容缩容)
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);

更灵活的ThreadPoolExecutor

实际上,上述工厂方法内部都是使用ThreadPoolExecutor构造的。直接使用它可更精细控制:

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                      // 核心线程数
    10,                     // 最大线程数
    60,                     // 空闲线程存活时间(秒)
    TimeUnit.SECONDS,       // 时间单位
    new ArrayBlockingQueue<>(100), // 任务队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

线程池重要参数

  1. corePoolSize:核心线程数,池中常驻线程数量
  2. maximumPoolSize:最大线程数,池中允许的最大线程数
  3. keepAliveTime:非核心线程空闲时的存活时间
  4. workQueue:任务队列,保存等待执行的任务
  5. threadFactory:创建线程的工厂
  6. handler:拒绝策略,当线程和队列都满时的处理方式

四种拒绝策略

  1. AbortPolicy(默认):直接抛出RejectedExecutionException异常
  2. CallerRunsPolicy:让提交任务的线程自己执行该任务
  3. DiscardPolicy:默默丢弃无法处理的任务
  4. DiscardOldestPolicy:丢弃队列中最老的任务,然后重试提交

使用示例

java 复制代码
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        
        // 提交10个任务
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            pool.execute(() -> {
                System.out.println("任务" + taskId + "正在执行,线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务" + taskId + "执行完毕");
            });
        }
        
        // 关闭线程池
        pool.shutdown();
    }
}

线程池生命周期

  1. RUNNING:接受新任务,处理队列任务
  2. SHUTDOWN:不接受新任务,但处理队列中的任务
  3. STOP:不接受新任务,不处理队列任务,中断正在执行的任务
  4. TIDYING:所有任务终止,workerCount为0
  5. TERMINATED:terminated()方法执行完毕

最佳实践

  1. 根据任务类型选择合适线程池:

    • CPU密集型:线程数 ≈ CPU核心数
    • IO密集型:线程数可以多一些(如CPU核心数×2)
  2. 避免使用无界队列,可能导致内存溢出

  3. 合理设置线程存活时间

  4. 为线程池中的线程设置有意义的名字,便于排查问题

  5. 考虑使用自定义的ThreadFactory,设置线程优先级、异常处理等

线程池是Java并发编程的重要工具,合理使用可以显著提高程序性能,但需要根据实际场景仔细配置参数。

相关推荐
心灵宝贝38 分钟前
IDEA 安装 SpotBugs 插件超简单教程
java·macos·intellij-idea
幼稚诠释青春43 分钟前
Java学习笔记(对象)
java·开发语言
小羊学伽瓦1 小时前
【Java基础】——JVM
java·jvm
老任与码1 小时前
Spring AI(2)—— 发送消息的API
java·人工智能·spring ai
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧1 小时前
MyBatis快速入门——实操
java·spring boot·spring·intellij-idea·mybatis·intellij idea
csdn_freak_dd2 小时前
查看单元测试覆盖率
java·单元测试
爱吃烤鸡翅的酸菜鱼2 小时前
【SpringMVC】详解cookie,session及实战
java·http·java-ee·intellij-idea
Wyc724092 小时前
JDBC:java与数据库连接,Maven,MyBatis
java·开发语言·数据库
老任与码2 小时前
Spring AI(3)——Chat Memory
java·人工智能·spring ai
贺函不是涵2 小时前
【沉浸式求职学习day36】【初识Maven】
java·学习·maven