java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java线程池的工作机制

Java线程池的工作机制详解

作者:高锰酸钾_

本文讲述了Java线程池的工作机制,包括线程池的创建、任务调度、线程复用、资源管理和拒绝策略,通过合理配置线程池参数,可以提升系统的并发性能和稳定性

Java线程池的工作机制

线程池是一种多线程管理机制,用于限制和控制并发线程的数量,以提升系统性能和资源利用率,降低频繁创建和销毁线程的开销,线程池是 Java 并发编程中的重要工具之一,广泛应用于高性能、多线程的场景

线程池通过复用已创建的线程执行多个任务,避免线程的频繁创建和销毁,线程池可以限制线程数量,防止大量线程导致的系统资源耗尽,线程池使用队列管理任务,支持任务调度和优先级,线程池提供策略处理超出能力范围的任务

我们可以使用Java提供的ThreadPoolExecutor类来创建一个线程池实例,让我们先来看一下他的构造方法:

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

可以看到他的构造方法有七个参数

那么为什么除了传入一个核心线程数之外,还要传入最大线程数呢?任务队列中又是哪些任务在排队等待呢?我们一起来探讨一下线程池的工作机制

假设创建一个核心线程为2,最大线程数为4的线程池:

ThreadPoolExecutor pool = new ThreadPoolExecutor(
            2,   // 核心线程数量
            4,   // 最大线程数量
            60,  // 空闲线程最大存活时间
            TimeUnit.SECONDS, // 单位
            new ArrayBlockingQueue<>(2), //创建任务队列
            Executors.defaultThreadFactory(),  // 创建线程工厂
            new ThreadPoolExecutor.AbortPolicy() // 任务拒绝策略
);

核心线程

如果同一时刻来了两个任务:任务1任务2,那么自然就会把两个任务交给两个核心线程去执行:

等待队列

如果同一时刻提交了4个新的任务,但是我们定义的线程池只有两个核心线程用来执行任务1任务2,此时核心线程全部繁忙,新任务会被放入等待队列,那么任务3任务4就会进入等待队列中等待,队列有容量限制,我们上面传入的阻塞队列容量是2:

临时线程

如果同一时刻提交的任务非常多,比如提交了6个任务,那么核心线程依旧会执行任务1任务2,由于此时核心线程全部繁忙,新任务会被放入等待队列,任务3任务4会被放如等待队列中等待执行

但是将任务5放入等待队列时,等待队列已满,线程池会尝试创建非核心线程:临时线程来执行新的任务,但是核心线程与临时线程的总和不得超过最大线程数量,也就是我们传入的4,所以此时线程池会创建2个临时线程来执行任务5任务6

临时线程用于处理高峰期任务,但是临时线程并不是核心线程,当临时线程处于空闲状态超过 keepAliveTime 后,临时线程就会被销毁

任务拒绝

如果任务数量超过了线程池最大处理能力(核心线程 + 临时线程 + 等待队列),则执行拒绝策略,例如同一时间提交了7个新任务,按照上面的运行机制,任务1任务2任务5任务6会被线程执行,任务3任务4会在等待队列中等待空闲线程

但是由于核心线程 + 临时线程 + 等待队列全部满员,此时任务7就会执行拒绝策略,共有四种拒绝策略:

任务拒绝策略说明
ThreadPoolExecutor.AbortPolicy默认策略 丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy抛弃等待队列中等待时间最久的任务,把当前任务加入等待队列
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行

这就是线程池的工作机制,线程池通过任务复用和资源管理提升了系统的并发性能,但使用时需结合具体场景合理配置参数,掌握线程池机制并深入理解其应用场景,将有效提升 Java 开发效率和系统稳定性

那么通常在我们的项目中,线程池的容量应该设置为多大呢?

线程容量

项目中的线程容量(即线程池的大小)设置需要基于项目的类型来制定,不同的项目设置不同的容量:

CPU密集型任务

对于主要依赖CPU计算的任务(如数据处理、图像渲染),线程数量建议接近或等于CPU核心数(包括逻辑核心)。

计算公式

这样可以充分利用CPU,但又不会因线程上下文切换过多而导致性能下降。

I/O密集型任务

对于涉及大量I/O操作的任务(如文件读写、网络请求、数据库查询),线程池可以设置为CPU核心数的多倍,因为I/O操作通常会让线程处于阻塞状态。

计算公式

更多线程可以隐藏I/O等待时间,提升系统吞吐量

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文