Java面试:Java线程池七大参数详解

Posted 陆海潘江小C

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试:Java线程池七大参数详解相关的知识,希望对你有一定的参考价值。

目录

写在前面

一、corePoolSize

二、maximunPoolSize

三、keepAliveTime

四、unit

五、workQueue

六、threadFactory

七、handler

最后总结


本篇内容共 2533 字,7307字符,阅读需要 5分钟。

写在前面

小伙伴们可能发现最近博主没更新文章,不过我还是会逛逛CSDN,定时回复私信问题的粉丝,因为博主最近赶上期末周,准备各种大作业,还要写实验文档等等QAQ。好消息就是,到今天博主的所有课程大作业基本完成了(〜^㉨^)〜,挤出时间更新一下我的博文,同时也是总结一下之前学习的知识,梳理学习Java线程池的一些内容,分享出来大家一起学习讨论!

相信像我一样的很多同学,没事刷刷面经,就会发现多线程在面试中出现很频繁,对于Java选手来说,线程池的知识肯定必不可少,今天我们就来详细了解Java线程池的七大参数,积累面试经验。

JDK1.8线程池参数源代码:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

一、corePoolSize

指的是核心线程大小,线程池中维护一个最小的线程数量,即使这些线程处于空闲状态,也一直存在池中,除非设置了核心线程超时时间

这也是源码中的注释说明。

/** @param corePoolSize the number of threads to keep in the pool, 
* even if they are idle, unless {@code allowCoreThreadTimeOut} is set.
*/

二、maximunPoolSize

指的是线程池中允许的最大线程数量。当线程池中核心线程都处理执行状态,有新请求的任务

1、工作队列未满:新请求的任务加入工作队列

2、工作队列已满:线程池会创建新线程,来执行这个任务。当然,创建新线程不是无限制的,因为会受到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.
*/

四、unit

是指空闲线程存活时间的单位。keepAliveTime的计量单位。枚举类型TimeUnit类

五、workQueue

1、ArrayBlockingQueue

基于数组的有界阻塞队列,特点FIFO(先进先出)。

当线程池中已经存在最大数量的线程时候,再请求新的任务,这时就会将任务加入工作队列的队尾,一旦有空闲线程,就会取出队头执行任务。因为是基于数组的有界阻塞队列,所以可以避免系统资源的耗尽

那么如果出现有界队列已满最大数量的所有线程都处于执行状态,这时又有新的任务请求,怎么办呢?

这时候会采用Handler拒绝策略,对请求的任务进行处理。后面会详细介绍。

2、LinkedBlockingQueue

基于链表的无界阻塞队列,默认最大容量Integer.MAX_VALUE( 2^{32}-1),可认为是无限队列,特点FIFO。

关于maximumPoolSize参数在工作队列为LinkedBlockingQueue时候,是否起作用这个问题,我们需要视情况而定!

情况①:如果指定了工作队列大小,比如core=2,max=3,workQueue=2,任务数task=5,这种情况的最大线程数量的限制是有效的。

情况②:如果工作队列大小默认2^{32}-1,这时maximumPoolSize不起作用,因为新请求的任务一直可以加到队列中。

3、PriorityBlockingQueue

优先级无界阻塞队列,前面两种工作队列特点都是FIFO,而优先级阻塞队列可以通过参数Comparator实现对任务进行排序,不按照FIFO执行

4、SynchronousQueue

不缓存任务的阻塞队列,它实际上不是真正的队列,因为它没有提供存储任务的空间。生产者一个任务请求到来,会直接执行,也就是说这种队列在消费者充足的情况下更加适合。因为这种队列没有存储能力,所以只有当另一个线程(消费者)准备好工作,put(入队)和take(出队)方法才不会是阻塞状态。


以上四种工作队列,跟线程池结合就是一种生产者-消费者 设计模式。生产者把新任务加入工作队列,消费者从队列取出任务消费,BlockingQueue可以使用任意数量的生产者和消费者,这样实现了解耦,简化了设计。

六、threadFactory

线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等。

官方使用默认的线程工厂源码如下:

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

Java 并发超出线程数和工作队列时候的任务请求处理策略,使用了策略设计模式

策略1:ThreadPoolExecutor.AbortPolicy(默认)

在默认的处理策略。该处理在拒绝时抛出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());
        }
    }

策略2:ThreadPoolExecutor.CallerRunsPolicy

调用 execute 方法的线程本身运行任务。这提供了一个简单的反馈控制机制,可以降低新任务提交的速度

    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();
            }
        }
    }

策略3:ThreadPoolExecutor.DiscardOldestPolicy

如果执行程序未关闭,则删除工作队列头部的任务,然后重试执行(可能再次失败,导致重复执行)。

    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);
            }
        }
    }

策略4:ThreadPoolExecutor.DiscardPolicy

无法执行的任务被简单地删除,将会丢弃当前任务,通过源码可以看出,该策略不会执行任务操作。

    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) {
        }
    }

最后总结

相信像我一样的很多同学,没事刷刷面经,就会发现多线程在面试中出现很频繁,对于Java选手来说,线程池的知识肯定必不可少,今天我们详细了解Java线程池的七大参数,积累面试经验。

其中,线程池的七大参数工作原理我进行了详细解析,阅读了《Java并发实战》这本书以及参考许多博文,发现里面存在许多细节,我通过综合考虑和判断,用最精准地表达解释了各个参数的具体作用,内容完全干货,赶紧收藏备战面试!

最后想跟大家说的是,学习Java必备的知识有哪些呢?很多粉丝私信我Java学习的路线,我推荐了这套知识图谱,粉丝们都觉得质量很不错!

学习Java开发,多线程等知识,里面有许多有趣的小程序可以做,最近我也在跟着这一套 《Java 工程师学习成长知识图谱》进行体系的学习,是CSDN官方推出的,质量很不错!

其中包含了Java专业体系结构完整详细,推荐给大家学习使用,有兴趣可以扫码查看,最近我也在学习当中,当然,我的文章会记录学习,欢迎大家阅读,比如我的专栏《Java宝藏》、《Socket网络编程》。

展开就是这样的,尺寸870mm*560mm排版好看,内容很充实。推荐给有需要的伙伴,一起来学习Java!


如果觉得不错欢迎“一键三连”哦,点赞收藏关注,评论提问建议,欢迎交流学习!一起加油进步,我们下篇见! 

本篇内容首发我的CSDN博客:https://csdn-czh.blog.csdn.net/article/details/118090737

 

以上是关于Java面试:Java线程池七大参数详解的主要内容,如果未能解决你的问题,请参考以下文章

Java线程池详解

Java线程池详解

Java 线程池详解

Java线程池详解

面试题2020-03-24Java线程池七个参数详解

线程池(详解):三大方法七大参数四种拒绝策略