java的线程池及动态线程池

Java 线程池(Thread Pool)是 Java 并发编程中的重要组件,主要用于管理和复用线程,以提高程序的并发性能、减少线程创建和销毁的开销,并防止资源耗尽。Java 提供了 java.util.concurrent 包下的 Executor 框架来管理线程池。


1. 为什么要使用线程池?

在高并发环境下,如果每个任务都创建一个新线程:

  1. 创建和销毁线程的开销大:每次创建线程都需要分配资源,销毁时需要回收资源。
  2. 线程资源不可控:如果无限制创建线程,可能会导致系统资源耗尽,如 CPU 过载或内存溢出。
  3. 线程调度成本高:线程数过多,线程之间的切换(上下文切换)会增加 CPU 负担。

线程池的好处:

  • 降低资源消耗:线程可复用,避免频繁创建和销毁线程。
  • 提高响应速度:任务提交后直接复用已有线程,减少创建新线程的延迟。
  • 提高可管理性:可以限制线程总数,防止无限制创建线程导致系统崩溃。
  • 提供任务排队机制:任务可在队列中等待,避免任务过载时无限创建线程。

2. Java 线程池的核心类

Java 提供了 Executor 框架来管理线程池,其中主要的类包括:

  • Executor(接口):是线程池的顶层接口,定义了执行任务的方法。
  • ExecutorService(接口):继承 Executor,提供更高级的线程池管理功能,如任务提交、关闭线程池等。
  • ThreadPoolExecutor(核心实现类):是 ExecutorService 的主要实现类,可以自定义线程池的大小、任务队列、拒绝策略等。
  • Executors(工具类):提供了一些预定义的线程池工厂方法(如 newFixedThreadPool())。

3. ThreadPoolExecutor 详解

ThreadPoolExecutor 是 Java 线程池的核心实现类,其构造方法如下:

public ThreadPoolExecutor(
    int corePoolSize,        // 核心线程数
    int maximumPoolSize,     // 最大线程数
    long keepAliveTime,      // 非核心线程的存活时间
    TimeUnit unit,           // 存活时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,       // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

3.1 参数解析:

  • corePoolSize(核心线程数)

    • 线程池中始终保持的线程数量,即使这些线程空闲也不会销毁。
    • 任务提交时,如果当前线程数小于 corePoolSize,则会创建新线程。
  • maximumPoolSize(最大线程数)

    • 线程池中允许的最大线程数,只有当任务队列满了,且核心线程都在忙碌时,才会创建超过 corePoolSize 的线程。
  • keepAliveTime & unit(非核心线程存活时间 & 时间单位)

    • 超过 corePoolSize 的线程(即非核心线程),如果在 keepAliveTime 时间内没有任务执行,就会被销毁。
    • unit 指定时间单位,如 TimeUnit.SECONDSTimeUnit.MILLISECONDS
  • workQueue(任务队列)

    • 存放等待执行的任务,常见的队列类型:
      1. ArrayBlockingQueue(有界队列):适用于任务量固定的场景,避免无限制堆积任务。
      2. LinkedBlockingQueue(无界队列):适用于任务量不确定的场景,但可能导致 maximumPoolSize 无效。
      3. SynchronousQueue(直接交付队列):不存储任务,任务提交后必须有线程立即执行,否则会创建新线程。
  • threadFactory(线程工厂)

    • 用于创建线程,可以自定义线程名称等,默认使用 Executors.defaultThreadFactory()
  • handler(拒绝策略)

    • 当任务队列满了,且线程数已达 maximumPoolSize 时,处理新任务的策略:
      1. AbortPolicy(默认策略):抛出 RejectedExecutionException,任务不会被执行。
      2. CallerRunsPolicy:由提交任务的线程执行任务(即调用 execute() 的线程自己执行)。
      3. DiscardPolicy:丢弃任务,不抛异常。
      4. DiscardOldestPolicy:丢弃队列中最老的任务,然后重新尝试执行新任务。

4. 线程池的执行流程

  1. 任务提交到线程池:

    • 如果当前线程数 < corePoolSize,创建新线程执行任务。
    • 如果当前线程数 ≥ corePoolSize,任务进入 workQueue 等待执行。
    • 如果 workQueue 满了,且当前线程数 < maximumPoolSize,创建新线程执行任务。
    • 如果 workQueue 满了,且当前线程数 ≥ maximumPoolSize,触发拒绝策略。
  2. 线程执行任务:

    • 线程从队列取出任务并执行。
    • 任务执行完后,线程继续获取下一个任务,或等待 keepAliveTime 时间。
  3. 线程池关闭:

    • shutdown():不接受新任务,等待已有任务执行完毕。
    • shutdownNow():试图中断正在执行的任务,清空任务队列。

5. 线程池的常见使用方式

5.1 使用 ThreadPoolExecutor 自定义线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

5.2 使用 Executors 创建线程池(不推荐)

Executors 提供了几个预定义的线程池:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

Executors 创建的线程池有一些缺陷,如 newFixedThreadPool()newSingleThreadExecutor() 可能导致 OOM(任务队列是 LinkedBlockingQueue,默认无界)。


6. 线程池的最佳实践

  1. 避免使用 Executors 创建线程池,改用 ThreadPoolExecutor 显式指定参数。
  2. 合理设置 corePoolSizemaximumPoolSize
    • CPU 密集型任务(如计算):corePoolSize = CPU 核心数maximumPoolSize = CPU 核心数 + 1
    • IO 密集型任务(如网络请求):corePoolSizemaximumPoolSize 可以适当调大,如 CPU 核心数 * 2
  3. 合理选择任务队列
    • 短任务:SynchronousQueue
    • 任务量较大:ArrayBlockingQueue(设置合理大小)
    • 任务不确定:LinkedBlockingQueue(需注意 OOM 风险)
  4. 合理选择拒绝策略,根据业务需求选择合适的拒绝处理方式。
  5. 手动关闭线程池,防止程序无法退出。

Java 动态线程池(Dynamic Thread Pool)详解

动态线程池指的是可以在运行时动态调整 ThreadPoolExecutor 的参数(如核心线程数、最大线程数、任务队列等),从而提高线程池的灵活性,适应不同负载需求。在 Java 业务场景中,工作负载通常是变化的,因此静态配置线程池可能导致资源浪费或性能瓶颈,而动态线程池能更好地应对这些问题。


1. 为什么需要动态线程池?

静态线程池在实际业务中存在以下问题:

  1. 负载变化导致资源浪费
    • 业务高峰时,固定大小的线程池可能不足,导致任务堆积或超时。
    • 业务低谷时,过多的空闲线程占用系统资源,降低整体性能。
  2. 任务队列积压
    • 如果任务执行速度慢,而 workQueue 容量有限,则任务可能被拒绝。
  3. 手动修改配置成本高
    • 业务需求变化时,如果 corePoolSizemaximumPoolSize 需要调整,通常需要重启应用,而动态调整可以避免这个问题。

2. 线程池参数动态调整

ThreadPoolExecutor 运行过程中,可以动态调整以下参数:

  • setCorePoolSize(int corePoolSize) → 调整核心线程数
  • setMaximumPoolSize(int maximumPoolSize) → 调整最大线程数
  • setKeepAliveTime(long time, TimeUnit unit) → 调整非核心线程存活时间
  • setRejectedExecutionHandler(RejectedExecutionHandler handler) → 更改拒绝策略

示例代码:动态调整线程池参数

import java.util.concurrent.*;

public class DynamicThreadPoolExample {
    public static void main(String[] args) throws InterruptedException {
        // 初始线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 5, 10, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        // 提交任务
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 正在执行任务");
                try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            });
        }

        // 观察线程池状态
        System.out.println("初始 corePoolSize:" + executor.getCorePoolSize());
        System.out.println("初始 maximumPoolSize:" + executor.getMaximumPoolSize());

        // 动态调整线程池参数
        Thread.sleep(5000);
        executor.setCorePoolSize(4);
        executor.setMaximumPoolSize(10);
        executor.setKeepAliveTime(5, TimeUnit.SECONDS);

        System.out.println("修改后 corePoolSize:" + executor.getCorePoolSize());
        System.out.println("修改后 maximumPoolSize:" + executor.getMaximumPoolSize());

        executor.shutdown();
    }
}

运行结果(部分日志):

pool-1-thread-1 正在执行任务
pool-1-thread-2 正在执行任务
初始 corePoolSize:2
初始 maximumPoolSize:5
修改后 corePoolSize:4
修改后 maximumPoolSize:10

说明

  • setCorePoolSize(4) 使线程池的核心线程数增加,提高并发能力。
  • setMaximumPoolSize(10) 允许更多线程在高负载时创建,防止任务堆积。
  • setKeepAliveTime(5, TimeUnit.SECONDS) 让非核心线程更快回收,降低低负载时的资源占用。

3. 结合 ScheduledExecutorService 定时调整线程池

可以使用 定时任务 来定期检测系统负载,并动态调整线程池大小。例如,可以根据 CPU 使用率、任务队列长度等指标调整 corePoolSizemaximumPoolSize

示例代码:定时调整线程池

import java.util.concurrent.*;

public class AdaptiveThreadPool {
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 10, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        // 定时任务,每 5 秒检查任务队列长度,动态调整线程池大小
        ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
        monitor.scheduleAtFixedRate(() -> adjustThreadPool(), 0, 5, TimeUnit.SECONDS);

        // 提交大量任务
        for (int i = 0; i < 50; i++) {
            executor.execute(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 执行任务");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    private static void adjustThreadPool() {
        int queueSize = executor.getQueue().size();
        System.out.println("当前队列大小:" + queueSize);

        if (queueSize > 5) {
            executor.setCorePoolSize(Math.min(executor.getCorePoolSize() + 1, executor.getMaximumPoolSize()));
            System.out.println("增大 corePoolSize: " + executor.getCorePoolSize());
        } else if (queueSize == 0 && executor.getCorePoolSize() > 2) {
            executor.setCorePoolSize(Math.max(executor.getCorePoolSize() - 1, 2));
            System.out.println("减少 corePoolSize: " + executor.getCorePoolSize());
        }
    }
}

运行效果

当任务量增加,队列变大时,线程池自动扩容;任务减少时,线程池自动缩容,避免资源浪费。


4. 配合 Spring Boot 读取动态配置

Spring Boot 中,可以使用 @Scheduled 任务动态调整线程池,并从配置中心(如 nacosApollo)获取最新配置。

示例:Spring Boot 结合 @Scheduled 调整线程池

@Component
public class DynamicThreadPoolManager {
    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Scheduled(fixedRate = 5000)
    public void adjustThreadPool() {
        int activeCount = executor.getActiveCount();
        int queueSize = executor.getThreadPoolExecutor().getQueue().size();
        System.out.println("当前活动线程:" + activeCount + ",任务队列:" + queueSize);

        if (queueSize > 5) {
            executor.setCorePoolSize(executor.getCorePoolSize() + 1);
            System.out.println("增加 corePoolSize:" + executor.getCorePoolSize());
        } else if (queueSize == 0 && executor.getCorePoolSize() > 2) {
            executor.setCorePoolSize(Math.max(executor.getCorePoolSize() - 1, 2));
            System.out.println("减少 corePoolSize:" + executor.getCorePoolSize());
        }
    }
}

5. 总结

方式适用场景适用方法
手动调用 setCorePoolSize()需要临时调整executor.setCorePoolSize(x)
定时任务调整负载随时间变化ScheduledExecutorService
Spring Boot + 配置中心业务需求随配置变化监听配置变更,调用 setCorePoolSize()

最佳实践

  1. 使用 ThreadPoolExecutor 代替 Executors 创建线程池,避免 OOM。
  2. 定时监控线程池负载,自动调整 corePoolSize,提升吞吐量。
  3. Spring Boot 结合配置中心,实时修改线程池参数,无需重启服务。

动态线程池适用于负载变化的场景,如 Web 服务器、微服务、高并发任务处理等。

ThreadPoolTaskExecutor 和 ThreadPoolExecutor 有什么区别

ThreadPoolTaskExecutorThreadPoolExecutor 都是 Java 线程池,但它们的应用场景、实现方式和功能有所不同。

对比项ThreadPoolExecutorThreadPoolTaskExecutor
java.util.concurrentorg.springframework.scheduling.concurrent
来源JDK 原生线程池Spring 封装的线程池
创建方式直接 new ThreadPoolExecutor(...)Spring 配置 @Beannew ThreadPoolTaskExecutor()
适用场景适用于所有 Java 应用(通用)主要用于 Spring 框架的异步任务
扩展性需要手动管理线程池支持 Spring 事件、生命周期管理
执行任务方式execute(Runnable) / submit(Callable)execute(Runnable) / submit(Callable),并支持 @Async

1. ThreadPoolExecutor(JDK 原生线程池)

ThreadPoolExecutor 是 Java 提供的核心线程池类,适用于所有 Java 应用程序,并且支持手动配置线程池参数。

示例:创建 ThreadPoolExecutor

import java.util.concurrent.*;

public class ThreadPoolExecutorExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 5, 10, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务");
            });
        }
        executor.shutdown();
    }
}

特点

  • 线程池参数可以完全自定义,例如核心线程数、最大线程数、任务队列等。
  • 适用于所有 Java 应用,而不仅限于 Spring。
  • 需要手动管理线程池的生命周期(调用 shutdown() 关闭)。

2. ThreadPoolTaskExecutor(Spring 线程池封装)

ThreadPoolTaskExecutor 是 Spring 基于 ThreadPoolExecutor 的封装,主要用于 Spring 框架,支持 @Async、生命周期管理,并能与 Spring 事件机制结合。

示例:Spring Boot 配置 ThreadPoolTaskExecutor

1)Java 配置方式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;

@Configuration
public class ThreadPoolConfig {

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(10);
        executor.setKeepAliveSeconds(10);
        executor.setThreadNamePrefix("async-task-");
        executor.initialize(); // 初始化线程池
        return executor;
    }
}
2)使用 @Async 异步执行任务
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {
    @Async
    public void executeAsyncTask() {
        System.out.println(Thread.currentThread().getName() + " 执行异步任务");
    }
}
3)开启 @Async 支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync // 启用异步
public class SpringBootApp {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootApp.class, args);
    }
}

特点

  • 与 Spring 兼容:自动管理线程池,支持 @Async 异步任务。
  • Spring 容器管理:不需要手动关闭线程池,Spring 会自动管理生命周期。
  • 支持 Executor 接口:可用作 Spring 的 @Async 任务执行器。

3. ThreadPoolExecutorThreadPoolTaskExecutor 的主要区别

对比项ThreadPoolExecutorThreadPoolTaskExecutor
创建方式new ThreadPoolExecutor(...) 手动创建通过 Spring @Bean 方式创建
适用范围任意 Java 应用适用于 Spring 项目
任务提交execute(Runnable)submit(Callable)兼容 JDK 方式,并支持 @Async
线程池管理需手动关闭(shutdown()由 Spring 管理,自动关闭
生命周期线程池独立运行受 Spring 容器管理
监控支持需手动实现监控可结合 Spring 监控

4. 什么时候用 ThreadPoolExecutor,什么时候用 ThreadPoolTaskExecutor

使用 ThreadPoolExecutor

适用场景

  • 纯 Java 应用(不依赖 Spring)。
  • 需要完全自定义线程池参数。
  • 需要手动管理线程池的生命周期。

🚫 不适用场景

  • Spring 项目中异步任务(建议使用 ThreadPoolTaskExecutor)。

使用 ThreadPoolTaskExecutor

适用场景

  • Spring Boot / Spring Cloud 项目,使用 @Async 进行异步任务执行。
  • 需要由 Spring 容器管理线程池,避免手动管理。
  • 需要与 Spring 事件监听结合(如 ApplicationEvent )。

🚫 不适用场景

  • 需要完全手动控制线程池(如自定义拒绝策略)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值