ThreadPoolExecutor参数 keepAliveTime allowCoreThreadTimeOut

2023-05-16

/**
 * Timeout in nanoseconds for idle threads waiting for work.
 * Threads use this timeout when there are more than corePoolSize
 * present or if allowCoreThreadTimeOut. Otherwise they wait
 * forever for new work.
 */
private volatile long keepAliveTime;

为等待工作的空闲线程设置的定时(以纳秒为单位)。
当存在超过 corePoolSize(即非核心线程)的线程或参数 allowCoreThreadTimeOut 为 true 时,线程使用此参数作为定时。
否则,他们会永远等待新的工作。

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

由源码得知,keepAliveTime 与 TimeUnit 一起搭配使用。最终将会转换为纳秒。

keepAliveTime的注释意思为,当线程数大于 corePoolSize 时,这是多余的空闲线程在终止前等待新任务的最长时间。

/**
 * If false (default), core threads stay alive even when idle.
 * If true, core threads use keepAliveTime to time out waiting
 * for work.
 */
private volatile boolean allowCoreThreadTimeOut;

如果为 false(默认值),则核心线程即使在空闲时也保持活动状态。
如果为 true,核心线程使用 keepAliveTime 定时等待工作。

/**
 * Sets the policy governing whether core threads may time out and
 * terminate if no tasks arrive within the keep-alive time, being
 * replaced if needed when new tasks arrive. When false, core
 * threads are never terminated due to lack of incoming
 * tasks. When true, the same keep-alive policy applying to
 * non-core threads applies also to core threads. To avoid
 * continual thread replacement, the keep-alive time must be
 * greater than zero when setting {@code true}. This method
 * should in general be called before the pool is actively used.
 *
 * @param value {@code true} if should time out, else {@code false}
 * @throws IllegalArgumentException if value is {@code true}
 *         and the current keep-alive time is not greater than zero
 *
 * @since 1.6
 */
public void allowCoreThreadTimeOut(boolean value) {
    if (value && keepAliveTime <= 0)
        throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
    if (value != allowCoreThreadTimeOut) {
        allowCoreThreadTimeOut = value;
        if (value)
            interruptIdleWorkers();
    }
}

从注释得知
当设置为 false,核心线程在缺少任务执行的情况下也不终止。
当设置为 true,核心线程的存活策略适用于非核心线程。
防止线程持续替换,当设置 allowCoreThreadTimeOut 为 true 时 keepAliveTime 必须大于0.
传入的参数值与 allowCoreThreadTimeOut 的默认值不同时,先将 allowCoreThreadTimeOut 进行赋值为 true(默认值为 false),再终止线程。

综合源码的意思得知

allowCoreThreadTimeOut 默认值为 false,核心线程在缺少任务执行的情况下也不终止。这样会一直占用 cpu。

allowCoreThreadTimeOut 设置为 true,核心线程的存活策略适用于非核心线程。

线程回收分两种情况

非核心线程回收

非核心线程没有任务的情况下,等待时间 > keepAliveTime 设置的值,会回收。

核心线程回收

核心线程没有任务的情况下,如果想要回收,需要做两个设置

allowCoreThreadTimeOut 为 true

keepAliveTime > 0

keepAliveTime设置成多少比较合适

从如下链接得知

https://www.zhihu.com/question/322212282/answer/667964027

1、keepAliveTime的存在的意义在于可以减少过于频繁的线程启动停止带来的性能消耗。

2、keepAliveTime的建议值在任何书籍、文档中都没有提到过。如果真的有建议值的话,我相信JDK会封装常量类。

3、keepAliveTime的值要视具体业务场景对待,如果这个线程池用在主业务逻辑上我觉得这个值设置为0也OK,如果这个线程池用在一个服务中且这个服务是单独一台机器部署的我觉得设置0也任然OK,如果这个线程池要执行的任务是非常密集型的我觉得这是0也同样OK。业务类型、业务权重、实施部署条件以及没一个具体的线程大概执行时间等都要考虑。

4、我见到过keepAliveTime设置为0,在线程体中根据条件来休眠线程的,我觉得这种方式也不错

这个看应用场景的,JDK自带的两种线程池构造:

newFixedThreadPool-0ms,固定大小的永不过期

newCachedThreadPool-60s,缓存大小的60秒过期

核心线程和非核心都用到这个变量值。设置回收核心线程时,这个值需要大于0。

考虑到实际情况,如果是普通任务后台执行时间很长的话,可以设置时长为1小时,1小时的时间是我接触过的很长的任务了,我做的一个项目通过定时任务后台执行就用了起码10分钟以上,好处是这个对时间要求不敏感,是夜里执行的任务。普通的web请求的话设置为1分钟即可。这样可以照顾到核心线程回收 keepAliveTime 设置的值需要大于0 的问题。

活跃执行任务的线程大致总数

/**
 * Returns the approximate number of threads that are actively
 * executing tasks.
 *
 * @return the number of threads
 */
public int getActiveCount() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        int n = 0;
        for (Worker w : workers)
            if (w.isLocked())
                ++n;
        return n;
    } finally {
        mainLock.unlock();
    }
}

已经调度执行的任务大致总数

/**
 * Returns the approximate total number of tasks that have ever been
 * scheduled for execution. Because the states of tasks and
 * threads may change dynamically during computation, the returned
 * value is only an approximation.
 *
 * @return the number of tasks
 */
public long getTaskCount() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        long n = completedTaskCount;
        for (Worker w : workers) {
            n += w.completedTasks;
            if (w.isLocked())
                ++n;
        }
        return n + workQueue.size();
    } finally {
        mainLock.unlock();
    }
}

已经完成的任务总数

/**
 * Returns the approximate total number of tasks that have
 * completed execution. Because the states of tasks and threads
 * may change dynamically during computation, the returned value
 * is only an approximation, but one that does not ever decrease
 * across successive calls.
 *
 * @return the number of tasks
 */
public long getCompletedTaskCount() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        long n = completedTaskCount;
        for (Worker w : workers)
            n += w.completedTasks;
        return n;
    } finally {
        mainLock.unlock();
    }
}

发现用到了全局变量 completedTaskCount

/**
 * Counter for completed tasks. Updated only on termination of
 * worker threads. Accessed only under mainLock.
 */
private long completedTaskCount;

由注释得知,是历史任务结束后的数量统计。

可以得知,想要知道当前任务有多少线程在执行,通过 getActiveCount() 可以得到。

获取任务队列大小

/**
 * Returns the task queue used by this executor. Access to the
 * task queue is intended primarily for debugging and monitoring.
 * This queue may be in active use.  Retrieving the task queue
 * does not prevent queued tasks from executing.
 *
 * @return the task queue
 */
public BlockingQueue<Runnable> getQueue() {
    return workQueue;
}

返回线程池中线程数量

/**
 * Returns the current number of threads in the pool.
 *
 * @return the number of threads
 */
public int getPoolSize() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // Remove rare and surprising possibility of
        // isTerminated() && getPoolSize() > 0
        return runStateAtLeast(ctl.get(), TIDYING) ? 0
            : workers.size();
    } finally {
        mainLock.unlock();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadPoolExecutor参数 keepAliveTime allowCoreThreadTimeOut 的相关文章

随机推荐