首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 中的线程池是什么 (面试必背)?

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

作者头像
水货程序员
修改2018-12-03 16:31:05
1.2K0
修改2018-12-03 16:31:05
举报
文章被收录于专栏:javathingsjavathings

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

不要问为什么,因为 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, 优先级) 执行。几乎不会用到这种线程池。

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档