Java 中的线程池是什么 (面试必背)?

这个文章不会涉及太深的线程知识(太深我也不懂)。这里只是把线程池的一些概念整理一下,当被问到这个题目的时候,尽可能背给面试官听就行了。

不要问为什么,因为 JVM 就是这么设计的,要问为什么,除了设计者本人,我觉得没人能回答的更好。

为什么要有线程池

任何需要大量创建的资源,都可以通过池化技术来缓解性能问题。线程池也是这种思想,提前创建好一批线程,使用的时候直接获取线程,而不需要临时创建,销毁,提高性能。连接池,常量池也是同一种思想。使用线程池的好处有以下几点,1. 避免重复创建和销毁线程,节省资源,提高性能。2,加强对线程的管理,避免无限制的创建线程,使用线程池,可以对线程统一分配,监控。

Java 自带的线程池

在 JDK1.5 之后,Java 自带了线程池,所以不需要自己去实现。创建线程池的核心方法就是 ThreadPoolExecutor 类的构造函数。

下面的代码就是 JVM 中的源码,看看注释就行了。

Java

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

该代码注释详细的描述了构造方法的参数意义,使用的时候可查 API 文档。但是面试的时候,需要背诵记忆的。

  • corePoolSize:核心池的大小。默认的,创建了线程池后,线程池中的线程数为 0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有核心线程。
  • maximumPoolSize:线程池最大线程数。这个参数通常是大于等于 corePoolSize,当线程数大于 corePoolSize 后,还有任务提交过来,则会被放入阻塞队列,当阻塞队列也满了之后,仍然有新任务提交过来,那么就会继续创建新线程,直到当前线程数大于 maximumPoolSize 后,则不再创建。(如果此时还有任务提交过来,那么就会采取拒绝策略)
  • keepAliveTime:闲置线程的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于 corePoolSize 时才有用;
  • unit:参数 keepAliveTime 的时间单位
  • workQueue:一个阻塞队列,用来存储等待执行的任务。 1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按 FIFO 排序任务; 2、LinkedBlockingQuene:基于链表结构的阻塞队列,按 FIFO 排序任务,吞吐量通常要高于 ArrayBlockingQuene; 3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene; 4、priorityBlockingQuene:具有优先级的无界阻塞队列;
  • threadFactory:ThreadFactory 是一个接口,通过线程工厂可以对线程的一些属性进行定制。
  • handler:RejectedExecutionHandler 是一个接口,它定义了拒如何拒绝创建新线程,即当线程数满的时候,还有新任务提交时的拒绝策略。默认的,提供了 4 种处理策略:
  1. ThreadPoolExecutor.AbortPolicy   默认的,抛出异常 RejectedExecutionException;
  2. ThreadPoolExecutor.CallerRunsPolicy 这个策略重试添加当前的任务。
  3. ThreadPoolExecutor.DiscardPolicy  忽略任务,没有异常信息;
  4. ThreadPoolExecutor.DiscardOldestPolicy 先将阻塞队列中第一个任务抛弃,把当前任务再加到队列。

通过线程工厂可以对线程的一些属性进行定制。

通过 ThreadPoolExecutor 类的构造方法创建线程池比较繁琐,Java 通过 Executors 提供 4 个静态方法,预设值了几种线程池。分别为:

  • newCachedThreadPool —— 线程数是无限大。keepAliveTime 为 60 秒,线程空闲置 60 秒就会被回收,它采用 SynchronousQueue 为阻塞队列,SynchronousQueue 的没有存储空间,意味着阻塞队列总是满的,所以当请求来的时候,如果没有闲置的线程,那么就新建一个线程。一般不太推荐使用,理论上,它可以榨干操作系统的线程资源。
  • newFixedThreadPool ——corePoolSize 和 maximunPoolSize 是相同的,所以它是一个线程池固定的线程池,可控制线程最大并发数。keepAliveTime 为 0,一旦有闲置线程,则立即回收掉。它采用 LinkedBlockingQueue 为阻塞队列,是一个无界队列,所以只要有任务来,都能排队进来,不会被拒绝。通常推荐使用这种线程池。
  • newScheduledThreadPool—— 创建一个定长线程池,支持定时及周期性任务的执行。它采用 DelayQueue 为阻塞队列,其中的任务只有到期时才能从队列中取出。
  • newSingleThreadExecutor—— 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序 (FIFO, LIFO, 优先级) 执行。几乎不会用到这种线程池。

上述就是背诵要点。具体实践的话,还是亲自写代码跑为好。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cmazxiaoma的架构师之路

通过了解RejectedExecutionException来分析ThreadPoolExecutor源码

观看本文章之前,最好看一下这篇文章熟悉下ThreadPoolExecutor基础知识。 1.关于Java多线程的一些常考知识点 2.看ThreadPoolE...

17320
来自专栏Android相关

Java线程池---addWorker方法解析

以上是addWorker方法的注释,大致意思如下: 该方法是用来创建,运行,清理Workers的。 检查是否一个新的Worker能否能够添加到当前状态以及给...

22530
来自专栏cmazxiaoma的架构师之路

关于Java多线程的一些常考知识点

23330
来自专栏Android机动车

线程池之小结

多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。

8230
来自专栏Java学习之路

Java中的阻塞队列

一丶什么是阻塞队列 阻塞队列(BlockingQueue)是一个支持两个可以进行阻塞插入和阻塞移除的附加方法的队列。 1)阻塞插入:当队列满后,队列会阻塞(...

37560
来自专栏Java编程技术

创建线程以及线程池时候要指定与业务相关的名字,以便于追溯问题

日常开发中当一个应用中需要创建多个线程或者线程池时候最好给每个线程或者线程池根据业务类型设置具体的名字,以便在出现问题时候方便进行定位,下面就通过实例来说明不设...

9510
来自专栏Android Note

Android – ThreadPool介绍

11220
来自专栏xingoo, 一个梦想做发明家的程序员

RequireJS源码初探

前两天跟着叶小钗的博客,看了下RequireJS的源码,大体了解了其中的执行过程。不过在何时进行依赖项的加载,以及具体的代码在何处执行,还没有搞透彻,奈何能力...

19890
来自专栏向治洪

Android 四种常见的线程池

引入线程池的好处 1)提升性能。创建和消耗对象费时费CPU资源 2)防止内存过度消耗。控制活动线程的数量,防止并发线程过多。 我们来看一下线程池的简单的构造 ...

20070
来自专栏lzj_learn_note

ThreadPoolExecutor学习笔记

Java有两个线程池类:ThreadPoolExecutor和ScheduledThreadPoolExecutor,且均继承于ExecutorService。...

1.6K60

扫码关注云+社区

领取腾讯云代金券