[线程]线程池的使用和7个参数的含义

目录

前言

线程池的参数含义

corePoolSize

maximumPoolSize

keepAliveTime

unit

workQueue

有几种工作队列?

ThreadFactory

handler

CallerRunsPolicy

AbortPolicy

DiscardPolicy

DiscardOldestPolicy

关于线程池配置?


前言

线程池是一个很重要的面试点。

面试前总是看看各种文章,好多时候都觉得筛选文章好坏好浪费时间。

不如自己整理整理,记记笔记,看看代码底层。

面试关心:

1. 参数都是什么意思

2. 有几种线程池,几种拒绝策略

3. 底层源码

4. 有哪些坑,线上遇到过问题吗?

线程池的参数含义

    /**
     * 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;
    }

corePoolSize

核心线程数

  • 线程池中会维护一个最小的线程数量,即使核心线程会一直存活,可能会空闲,也不会被销毁(除非设置allowCoreThreadTimeout)
  • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
  • * @param corePoolSize the number of threads to keep in the pool, even
    *        if they are idle(空闲), unless {@code allowCoreThreadTimeOut} is set
  • 官方英文释义:等于前两点

maximumPoolSize

  • * @param maximumPoolSize the maximum number of threads to allow in the
    *        pool
  • 英文释义:最大的数量可以允许在这个线程池的
  • 一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程
  • 其实好理解点就是:这东西是先看核心线程数,比如10个,这个maximumPoolSize假如是20,那么任务先会缓存到工作队列里面,工作队列满了,才会额外开启线程,如果是20那么最多开启10个
  • 然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
  • maximumPoolSize最佳实践是跟核心线程数保持一致

keepAliveTime

  • * @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.
  • 中文释义:线程数,大于核心数,那部分线程会过期
  • 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
  • 核心线程不受这个影响

unit

  • * @param unit the time unit for the {@code keepAliveTime} argument
  • keepAliveTime的时间单元:没啥好说的

workQueue

  • * @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.
  • 中文释义:队列用于任务执行,可以Runnable的方法进行执行之后,会先放到这个队列里面
  • 新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。

有几种工作队列?

1.ArrayBlockingQueue

基于数组的有界阻塞队列,按FIFO排序。

有界数组,如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略

2.LinkedBlockingQuene

基于链表的无界阻塞队列,最大容量为Integer.MAX( 2147483647),按照FIFO排序。

相当于无界队列了,还是注意使用。参数maxPoolSize其实是不起作用的(因为不管咋样,工作队列是放不满的)。

/**
 * A constant holding the minimum value an {@code int} can
 * have, -2<sup>31</sup>.
 */
@Native public static final int   MIN_VALUE = 0x80000000;

/**
 * A constant holding the maximum value an {@code int} can
 * have, 2<sup>31</sup>-1.
 */
@Native public static final int   MAX_VALUE = 0x7fffffff;

3.SynchronousQueue

灭有缓存,直接就进来就用空闲线程执行,没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

4.PriorityBlockingQueue

具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

BlockingQuene的引用

ThreadFactory

负责生产线程

 项目中其实用的是磨人的线程工厂,和默认的拒绝策略。

 

* Constructs a new {@code Thread}.  Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.

也就是说,可以设定线程的优先级,名称 和守护线程等。

   /**
     * The default thread factory
     */
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

其实就是设置守护线程和优先级。

handler

拒绝策略:

/**
 * The default rejected execution handler
 */
private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();

CallerRunsPolicy

    /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

      * Executes task r in the caller's thread, unless the executor
     * has been shut down, in which case the task is discarded.

直接跑被拒绝任务的run方法,,除非线程池已经shutdown,则直接抛弃任务。

AbortPolicy


    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

Always throws RejectedExecutionException.

任务就丢弃了

DiscardPolicy


    /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

Does nothing, which has the effect of discarding task r.

啥都不做,直接丢弃任务

DiscardOldestPolicy

    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

 抛弃进入队列最早的任务

尝试把这次拒绝的任务放入队列

关于线程池配置?

关于线程池配置这部分转自:https://blog.csdn.net/sxllllwd/article/details/100533788

作者:沙漏dan

CPU密集型和IO密集型
CPU密集型也是指计算密集型,大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务。该类型的任务需要进行大量的计算,主要消耗CPU资源。

这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。(减少任务切换)

IO密集型任务指任务需要执行大量的IO操作,涉及到网络、磁盘IO操作,对CPU消耗较少。

结论:
CPU密集型任务应配置尽可能小的线程,如配置CPU数目+1个线程的线程池。

由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*CPU数目。
 

阿里的线程池推荐

其实就是用底层的代码,自己设置参数。

比如:

Executors工厂创建线程池,不支持自定义拒绝策略。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

    }

可以参考这种:https://www.jianshu.com/p/14298f302bb2

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值