ThreadPoolExecutor线程池的keepAliveTime的具体含义

导读:本篇文章讲解 ThreadPoolExecutor线程池的keepAliveTime的具体含义,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

keepAliveTime含义

ThreadPoolExecutor 的 javadoc 对 keepAliveTime 参数的解释:

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.

大致意思是:keepAliveTime 就是 当线程数大于核心线程数时,在终止前,多余的空闲线程等待新任务的最长时间。

大白话说明

假设,核心线程数10,最大线程数30,keepAliveTime是 3秒

随着任务数量不断上升,线程池会不断的创建线程,直到到达核心线程数10,就不创建线程了,这时多余的任务通过加入阻塞队列来运行,当超出(阻塞队列长度+核心线程数)时,

这时不得不扩大线程个数来满足当前任务的运行,这时就需要创建新的线程了(最大线程数起作用),上限是最大线程数30,

那么,超出核心线程数10并小于最大线程数30的可能新创建的这20个线程相当于是”借”的,如果这20个线程空闲时间超过keepAliveTime,就会被退出

注:核心线程的关闭 由 ThreadPoolExecutor#allowCoreThreadTimeout 决定,默认 allowCoreThreadTimeout=false,代表不会 超时终止,就是不受 KeepLive的影响。

public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * If false (default), core threads stay alive even when idle.
     * If true, core threads use keepAliveTime to time out waiting
     * for work.
     */
    // 全局变量,自动初始化,boolean 类型默认是 false
    private volatile boolean allowCoreThreadTimeOut;

    // 略

    // 设置 核心线程是否可以 超时终止,默认 allowCoreThreadTimeOut=false
    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();
        }
    }
    // 略
}

但是我有两个疑问

  1. 线程为什么会空闲

    没有任务时线程就会空闲下来,在线程池中任务是任务(Runnale)线程是线程(Worker)

  2. 线程为什么要退出

    通常超出核心线程的线程是“借”的,也就是说超出核心线程的情况算是一种能够预见的异常情况,并且这种情况并不常常发生(如果常常发生,那我想你应该调整你的核心线程数了),所以这种不经常发生而创建的线程为了避免资源浪费就应该要退出

我们需要看一下java.util.concurrent.ThreadPoolExecutor#getTask源码和javadoc 注释来验证上面一段话的含义:

ThreadPoolExecutor#getTask javadoc

/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
*    a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
*    workers are subject to termination (that is,
*    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
*    both before and after the timed wait, and if the queue is
*    non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
*         workerCount is decremented
*/

大致意思:

执行任务阻塞或时间等待,具体取决于当前配置设置,如果此工作线程返回 null
就必须退出,可能是因为以下任何一个原因造成:

  1. 这里有超过最大线程池大小的工作线程(最大线程池大小 由 setMaximumPoolSize 设置)。
  2. 线程池已经停止了。
  3. 线程池已关闭,并且阻塞队列为空。
  4. 工作线程等待任务,已经超时了,那么超时的工作线程将会被终止。(代码描述:allowCoreThreadTimeOut || workerCount > corePoolSize)

ThreadPoolExecutor#getTask 源码

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        // 当allowCoreThreadTimeOut为true或者当前任务数超过核心线程数时,timed为true
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            //如果timed为true说明worker有可能要被关闭,
            // 这里调用的代码含义:
            //如果超过keepAliveTime纳秒内还没取到任务,就返回null,后面会调用processWorkerExit把worker关闭
            // 如果timed为false,
            // 就阻塞在这里,直到任务队列再有任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

keepAliveTime 测试

  1. 测试:
    设置 线程池 核心线程数=1,最大线程数=1,空闲线程最大存活时间=0(立即终止),阻塞队列长度=1
    按循序,立即提交三个任务,依次分别命名为 1,2,3
    任务处理时间 3s

  2. 执行步骤:
    任务1 首先提交,立即创建线程 ,执行任务1;
    任务2 提交时,任务1还未完成,于是,将任务2,保存到 阻塞队列中
    任务3 提交,任务1,未完成,任务2 还在阻塞队列中等待,
    因为此时已经达到核心线程数,并且阻塞队列已经满了
    还没有达到最大线程数,因此,创建新线程执行任务3
    任务 1 首先结束
    然后,任务3 结束,因为 keepLive=0,所以,执行完任务 3 的线程,将会立即被终止
    最后,执行完任务 1 的线程空闲,但是默认核心线程不会超时终止,所以,执行完任务 1 的线程继续继续执行任务 2
    任务 2 结束
    线程池 shutdown

public class keepAliveTimeTest {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = null;
        try {
            // 实例化线程池
            pool = new ThreadPoolExecutor(1,2,
                                    0L, TimeUnit.MILLISECONDS,
                                            new ArrayBlockingQueue<>(1));

            // 未提交任何任务的线程池的状态
            System.out.println("Core threads: " + pool.getCorePoolSize());
            System.out.println("Largest executions: "
                    + pool.getLargestPoolSize());
            System.out.println("Maximum allowed threads: "
                    + pool.getMaximumPoolSize());
            System.out.println("Current threads in pool: "
                    + pool.getPoolSize());
            System.out.println("Currently executing threads: "
                    + pool.getActiveCount());
            System.out.println("Total number of threads(ever scheduled): "
                    + pool.getTaskCount());

            pool.execute(new Task("1"));
            pool.execute(new Task("2"));
            pool.execute(new Task("3"));

            System.out.println("========================================");
            // 阻塞 9s,线程池所有任务结束,阻塞 1s,看看核心线程和”借来“的线程的状态
            TimeUnit.SECONDS.sleep(10);
            System.out.println("========================================");

            // 执行完所有任务的线程池的状态
            System.out.println("Core threads: " + pool.getCorePoolSize());
            System.out.println("Largest executions: "
                    + pool.getLargestPoolSize());
            System.out.println("Maximum allowed threads: "
                    + pool.getMaximumPoolSize());
            System.out.println("Current threads in pool: "
                    + pool.getPoolSize());
            System.out.println("Currently executing threads: "
                    + pool.getActiveCount());
            System.out.println("Total number of threads(ever scheduled): "
                    + pool.getTaskCount());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 等待所有任务结束,才会关闭线程池
            assert pool != null;
            pool.shutdown();
        }


    }

    static class Task implements Runnable {

        private String name;

        public Task(String name){
            this.name = name;
        }

        public void run() {
            try {
                System.out.println(name + " Task running! [" + Thread.currentThread().getName() + "]");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(name + " Task Completed! ["+ Thread.currentThread().getName() + "]");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试结果:

Core threads: 1
Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
========================================
1 Task running! [pool-1-thread-1]
3 Task running! [pool-1-thread-2]
3 Task Completed! [pool-1-thread-2]
1 Task Completed! [pool-1-thread-1]
2 Task running! [pool-1-thread-2]
2 Task Completed! [pool-1-thread-2]
========================================
Core threads: 1
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 1
Currently executing threads: 0
Total number of threads(ever scheduled): 3

可以看到,Current threads in pool = 1,所以说明,KeepLive=0时,”借来”的工作线程是不等待直接退出的。而不是永远不退出。

注意: KeepLive设置为0,并不是个很好的做法(除非场景中任务数量极少能超出核心线程数),如果任务数频繁超出核心线程数,这个值需要评估设定为合理值尽量避免线程开启关闭的动作。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69684.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!