ThreadPoolExecutor参数解析

文章介绍了Java中ThreadPoolExecutor的构造函数参数及其作用,展示了如何创建一个自定义线程池`CustomThreadPool`,并提供了批量与单个任务提交的方法。通过测试案例分析了线程池在不同任务数量下的行为,包括任务队列满时的拒绝策略。强调了正确设置线程池参数和队列长度的重要性。

一、参数

构造函数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

参数

参数名说明
corePoolSize核心线程数量(常驻)

maximumPoolSize

最大线程数量

keepAliveTime

临时线程存活时间

unit

存活时间单位

workQueue

队列

threadFactory

线程池创建线程对应的工厂

二、源码示例

定义一个线程池包装类(方便使用,也可以使用时候直接创建)

package com.hz.threadpool;

import java.io.Closeable;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description:
 * @author: pp_lan
 * @date: 2022/3/15
 */
public class CustomThreadPool implements Closeable {

    private ThreadPoolExecutor pool;

    private AtomicInteger count = new AtomicInteger();

    private CustomThreadPool(int core, int max, int queueSize, String threadName) {
        this.pool = new ThreadPoolExecutor(core, max, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(queueSize),
                r -> new Thread(r, threadName + count.getAndIncrement()));
    }

    public ThreadPoolExecutor getPool() {
        return pool;
    }

    /**
     * 获取对象
     * @param core
     * @param max
     * @param queueSize
     * @param threadName
     * @return
     */
    public static CustomThreadPool initPool(int core, int max, int queueSize, String threadName) {
        return new CustomThreadPool(core, max, queueSize, threadName);
    }

    /**
     * callable批量任务
     * @param tasks
     * @param <T>
     * @return
     */
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
        try {
            List<Future<T>> futures = this.pool.invokeAll(tasks);
            return futures;
        } catch (InterruptedException e) {
            throw new RuntimeException("线程被中断", e);
        }
    }

    /**
     * callable单个任务
     * @param task
     * @param <T>
     * @return
     */
    public <T> Future<T> submit(Callable<T> task) {
        return this.pool.submit(task);
    }

    /**
     * runnable任务
     * @param runnable
     */
    public void execute(Runnable runnable) {
        this.pool.execute(runnable);
    }

    /**
     * runnable任务
     * @param runnable
     */
    public Future<?> submit(Runnable runnable) {
        return this.pool.submit(runnable);
    }

    /**
     * 线程池关闭,建议使用try-resource进行自动关闭,不要手动执行
     */
    public void shutdown() {
        this.pool.shutdown();
        try {
            if (!this.pool.awaitTermination(1, TimeUnit.MINUTES)) {
                this.pool.shutdownNow();
                if (!this.pool.awaitTermination(1, TimeUnit.MINUTES)) {
                    throw new RuntimeException("线程池关闭失败");
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("线程池关闭失败", e);
        }
    }

    @Override
    public void close() {
        this.shutdown();
    }
}

三、测试

依赖

package com.hz.task;

import java.util.concurrent.TimeUnit;

/**
 * 任务
 *
 * @author pp_lan
 */
public class Task implements Runnable {

    private int index;

    public Task(int index) {
        this.index = index;
    }

    @Override
    public void run() {
        System.out.println("-------------执行任务" + index);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

参数调试

taskSize <= coreSize + queueSize

package com.hz.task;

import com.hz.threadpool.CustomThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

/**
 * 线程池调参
 *
 * @author pp_lan
 */
public class ThreadPoolParamFixedDemo {

    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolParamFixedDemo.class);

    public static void main(String[] args) {
        Queue<Runnable> queue = new LinkedBlockingDeque<>();
        try (CustomThreadPool pool = CustomThreadPool.initPool(2, 20, 5, "线程池")) {
            for (int i = 0; i < 7; i++) {
                queue.add(new Task(i));
            }

            new Thread(() -> {
                while (true) {
                    Runnable task = queue.poll();
                    if (task != null) {
                        pool.execute(task);
                    } else {
                        try {
                            TimeUnit.SECONDS.sleep(2);
                        } catch (InterruptedException e) {
                            LOGGER.error("休眠被中断", e);
                        }
                    }
                }
            }).start();

            int activeCount = 1;
            while (activeCount > 0) {
                try {
                    System.out.println("wait...");
                    TimeUnit.SECONDS.sleep(1);
                    activeCount = pool.getPool().getActiveCount();
                } catch (InterruptedException e) {
                    LOGGER.error("中断异常", e);
                }
            }
        }
    }

}

运行结果:

wait...
-------------执行任务0
-------------执行任务1
wait...
-------------执行任务2
-------------执行任务3
wait...
-------------执行任务4
-------------执行任务5
wait...
-------------执行任务6
wait...
wait...

此时,从运行结果可见:线程数量=coreSize。

coreSize + queueSize < taskSzie <= maxSize + queueSize

创建新的线程

public static void main(String[] args) {

        ...

        for (int i = 0; i < 25; i++) {
            queue.add(new Task(i));
        }

        ...

    }

运行结果:

wait...
-------------执行任务0
-------------执行任务1
-------------执行任务7
-------------执行任务8
-------------执行任务9
-------------执行任务10
-------------执行任务11
-------------执行任务12
-------------执行任务13
-------------执行任务14
-------------执行任务15
-------------执行任务16
-------------执行任务17
-------------执行任务19
-------------执行任务20
-------------执行任务21
-------------执行任务22
-------------执行任务18
-------------执行任务23
-------------执行任务24
wait...
-------------执行任务2
-------------执行任务3
-------------执行任务4
-------------执行任务5
-------------执行任务6
wait...
wait...
wait...
Disconnected from the target VM, address: '127.0.0.1:35445', transport: 'socket'

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

此时,运行结果可见:coreSize <= 线程数量<=maxSize。

taskSzie > maxSize + queueSize

public static void main(String[] args) {

        ...

        for (int i = 0; i < 26; i++) {
            queue.add(new Task(i));
        }

        ...

    }

运行结果:

-------------执行任务21
-------------执行任务22
-------------执行任务24
-------------执行任务23
Exception in thread "Thread-0" java.util.concurrent.RejectedExecutionException: Task com.hz.task.Task@73c8c9df rejected from java.util.concurrent.ThreadPoolExecutor@625fdb98[Running, pool size = 20, active threads = 20, queued tasks = 5, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.hz.threadpool.CustomThreadPool.execute(CustomThreadPool.java:72)
	at com.hz.task.TaskQueue.lambda$main$0(TaskQueue.java:33)
	at java.lang.Thread.run(Thread.java:748)
wait...

任务满了,多余任务添加入线程池则执行拒绝策略

四、总结

以demo中的数据为例,coreSize(2), queueSize(5), maxSize(20),实时任务和线程数量对应关系如下所示:

实时任务数量线程数量
[0, 7]2
(7, 25](2, 20]
(25, +\infty)(2, 20], 且多余任务执行拒绝策略,例如报错

注意:

因为只有当taskSize > coreSize + queueSize才会创建新线程,所以创建线程池时候队列需要指定队列长度(反例: new LinkedBlockingDeque()),否则会导致线程数一直为核心线程,即使任务阻塞甚至被拒绝也无法扩展到最大核心数对应的线程数量

LinkedBlockingDeque对应构造函数如下:

public LinkedBlockingDeque() {
    this(Integer.MAX_VALUE);
}

<think>首先,用户的问题是:“我想了解JavaThreadPoolExecutor类各个参数及其含义 请问Java ThreadPoolExecutor类各参数详解”。用户要求详细解释Java ThreadPoolExecutor类的各个参数及其含义。 从系统级指令看: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。 用户提供了几个引用: - [1] Java ThreadPoolExecutor 详解,提到核心参数。 - [2] java ThreadPoolExecutor核心参数深度解析与调优指南,提到七大核心参数。 - [3] Java线程池ThreadPoolExecutor参数、拒绝策略)详解,提到参数和拒绝策略。 - [4] Java ThreadPoolExecutor 深度解析,提到继承关系。 - [5] ThreadPoolExecutor类参数简述,提到核心线程与临时线程的类比。 我需要在回答中参考这些引用,并在适当的地方添加引用标识。 ThreadPoolExecutor的关键参数包括: - corePoolSize: 核心线程数。 - maximumPoolSize: 最大线程数。 - keepAliveTime: 线程空闲时间。 - unit: 时间单位(如秒、毫秒)。 - workQueue: 工作队列(如BlockingQueue)。 - threadFactory: 线程工厂。 - handler: 拒绝策略处理器。 从引用中: - [2] 提到七大核心参数:corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler. - [5] 用类比解释核心线程和临时线程。 回答结构: 1. **介绍ThreadPoolExecutor**:简要介绍类的作用。 2. **详细解释每个参数**: - corePoolSize - maximumPoolSize - keepAliveTime - unit - workQueue - threadFactory - handler (RejectedExecutionHandler) 3. **工作原理**:解释线程池如何工作,基于参数。 4. **最佳实践或注意事项**:基于引用。 5. **生成相关问题**:在最后添加相关问题。 确保使用中文。 添加引用标识: - 例如,当引用[2]时,在句子末尾加[^2]。 生成相关问题: - 如指令所示,在最后添加"§§相关问题§§"后列出几个问题。 最终输出结构: - 回答部分。 - 相关问题部分。 </think> ### Java ThreadPoolExecutor类参数详解 ThreadPoolExecutorJava 并发包 (`java.util.concurrent`) 中的核心类,用于高效管理线程池,减少线程创建和销毁的开销,提高系统性能和资源利用率。它通过灵活的配置参数实现任务调度和线程管理,适用于高并发场景。下面我将详细解释其核心参数及其含义,基于标准 Java 文档和最佳实践[^2][^3]。ThreadPoolExecutor 的构造函数通常包含以下七个参数: #### 1. **corePoolSize(核心线程数)** - **含义**:指定线程池中保持活动状态的最小线程数。即使这些线程处于空闲状态,也不会被销毁,除非设置了 `allowCoreThreadTimeOut` 参数[^5]。 - **作用**:作为线程池的“正式员工”,确保系统在低负载时有基本线程处理任务。例如,如果 `corePoolSize = 5`,线程池始终至少保持 5 个线程运行。 - **配置原则**:通常设置为平均任务负载所需的最小线程数,过高会导致资源浪费,过低会影响响应速度[^2]。 #### 2. **maximumPoolSize(最大线程数)** - **含义**:指定线程池允许创建的最大线程数,包括核心线程和临时线程。当任务队列满时,线程池会创建新线程,但总数不超过此值[^3]。 - **作用**:作为“临时员工”的上限,应对突发高负载。例如,如果 `corePoolSize = 5` 且 `maximumPoolSize = 10`,线程池最多可创建 5 个额外线程。 - **配置原则**:需考虑系统资源(如 CPU 核心数),避免过高导致内存溢出或线程竞争[^2]。 #### 3. **keepAliveTime(线程空闲时间)** - **含义**:指定非核心线程(即超出 `corePoolSize` 的线程)在空闲状态下的存活时间。当线程空闲超过此时间,会被销毁,直到线程数降至 `corePoolSize`[^3][^5]。 - **作用**:优化资源利用,防止空闲线程占用过多内存。例如,`keepAliveTime = 60` 表示非核心线程空闲超过 60 单位时间后销毁。 - **配置原则**:基于任务特性设置,短时间任务可设较低值(如几秒),长时间任务可设较高值。 #### 4. **unit(时间单位)** - **含义**:指定 `keepAliveTime` 的时间单位,是 `java.util.concurrent.TimeUnit` 枚举值(如 `TimeUnit.SECONDS`、`TimeUnit.MILLISECONDS`)。 - **作用**:定义 `keepAliveTime` 的粒度。例如,`unit = TimeUnit.SECONDS` 表示时间以秒计。 - **配置原则**:通常与 `keepAliveTime` 配合,选择合理单位以确保精确控制线程生命周期[^1]。 #### 5. **workQueue(工作队列)** - **含义**:指定任务队列,用于存储待执行任务,是 `BlockingQueue<Runnable>` 实现(如 `LinkedBlockingQueue`、`ArrayBlockingQueue` 或 `SynchronousQueue`)[^4]。 - **作用**:作为缓冲区,当任务数超过 `corePoolSize` 时,新任务入队等待;队列满时触发创建新线程或拒绝策略。例如,无界队列(如 `LinkedBlockingQueue`)可平滑处理任务高峰。 - **配置原则**:队列类型影响性能,无界队列可能导致内存溢出,有界队列需合理设置容量[^2]。 #### 6. **threadFactory(线程工厂)** - **含义**:指定 `ThreadFactory` 接口实现,用于创建新线程[^2][^4]。 - **作用**:自定义线程属性,如线程名、优先级或守护线程状态。例如,可设置线程名以方便调试。 - **配置原则**:默认使用 `Executors.defaultThreadFactory()`,但自定义工厂可增强监控和错误处理。 #### 7. **handler(拒绝策略处理器)** - **含义**:指定 `RejectedExecutionHandler` 接口实现,用于处理当任务队列满且线程数达到 `maximumPoolSize` 时拒绝的任務。 - **作用**:定义拒绝行为,Java 提供四种内置策略: - `AbortPolicy`:直接抛出 `RejectedExecutionException`(默认)。 - `CallerRunsPolicy`:由提交任务的线程直接执行任务。 - `DiscardPolicy`:静默丢弃任务。 - `DiscardOldestPolicy`:丢弃队列中最旧的任务,然后重试。 - **配置原则**:根据系统要求选择,例如高可用系统可选 `CallerRunsPolicy` 避免任务丢失。 ### ThreadPoolExecutor 工作原理简述 线程池执行任务时遵循以下流程[^1][^5]: 1. 任务提交后,优先由核心线程执行。 2. 如果核心线程全忙,任务入队到 `workQueue`。 3. 如果队列满,且线程数未达 `maximumPoolSize`,创建新线程(临时线程)。 4. 如果线程数已达 `maximumPoolSize` 且队列满,触发 `handler` 拒绝策略。 5. 非核心线程空闲超过 `keepAliveTime` 后被销毁。 这种机制平衡了资源利用和响应速度,核心线程提供稳定性,临时线程应对峰值[^5]。 ### 最佳实践与注意事项 - **参数调优**:监控系统负载调整参数,例如在 CPU 密集型任务中,`corePoolSize` 可设为 CPU 核心数;I/O 密集型任务可适当增大 `maximumPoolSize`[^2]。 - **避免资源泄露**:使用有界队列防止 OOM,并设置合理的 `keepAliveTime`[^3]。 - **线程工厂使用**:自定义 `threadFactory` 添加线程异常处理,提升健壮性。 - **拒绝策略选择**:在生产环境中,优先使用自定义策略或 `CallerRunsPolicy` 保证任务不丢失。 通过合理配置这些参数,ThreadPoolExecutor 能显著提升并发性能,适用于 Web 服务器、批处理系统等场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值