Java线程池面试要点

  1. maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程。 当线程数量达到corePoolSize,且workQueue队列塞满任务了之后,继续创建线程 ,当线程池中的线程数量到达这个数字时,新来的任务会执行拒绝策略。

  2. keepAliveTime:表示线程没有任务执行时最多能保持多少时间会被回收,注意,这个参数控制的是超过corePoolSize之后的“临时线程”的存活时间。

  3. unit:参数 keepAliveTime 的时间单位。

  4. workQueue:工作队列,存放提交的等待任务,其中有队列大小的限制。

  5. threadFactory:创建线程的工厂类,通常我们会自定义一个threadFactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位排查问题。

  6. handler:如果线程池已满,新的任务进来时的拒绝策略。

ThreadPoolExecutor 参数含义是最常见的一个问题,如果面试者对这些参数比较了解,至少说明面试者在多线程运用层面不会存在太大的问题,反之,如果面试官提示某个参数后面试者还是一脸懵的话,那么基础印象分就会大打折扣。

线程池线程创建的流程是怎样的

线程池线程创建的时机可以用下面这张图简单表示。

线程创建流程是这样的:

  1. 如果当前运行的线程少于corePoolSize(核心线程数),则创建新线程来执行任务(执行这一步骤需要获取全局锁)。

  2. 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue(阻塞队列/任务队列)。

  3. 如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(执行这一步骤也需要获取全局锁)。

  4. 如果创建新线程将使得当前运行的线程超出maximumPoolSize限制,任务将被拒绝,并执行线程饱和策略,如:RejectedExecutionHandler.rejectedExecution()方法。

注意:初始化线程池时,线程数为0。

工作列队有哪几种实现

存放任务的工作队列有6种主要的实现,分别是 ArrayBlockingQueue、LinkedBlockingQueue、LinkedBlockingDeque、PriorityBlockingQueue、DelayQueue、SynchronousQueue。它们的区别如下:

  1. ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。

  2. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列,在未指明容量时, 容量默认为 Integer.MAX_VALUE 。

  3. LinkedBlockingDeque:使用双向队列实现的双端阻塞队列,双端意味着可以像普通队列一样 FIFO(先进先出),可以以像栈一样 FILO(先进后出)

  4. PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较,跟时间没有任何关系,仅仅是按照优先级取任务。

  5. DelayQueue:同 PriorityBlockingQueue,也是二叉堆实现的优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。

  6. SynchronousQueue:一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用put()方法的时候就会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。

拒绝策略有哪几种

线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理地处理新进来的任务。JDK 内置的四种拒绝策略如下:

  1. AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。

  2. CallerRunsPolicy:由调用线程处理该任务。(例如io操作,线程消费速度没有NIO快,可能导致阻塞队列一直增加,此时可以使用这个模式)。

  3. DiscardPolicy:丢弃任务,但是不抛出异常。(可以配合这种模式进行自定义的处理方式)。

  4. DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

线程池的分类

Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具,真正的线程池接口是  ExecutorService。Java中 Executors 工厂类可以为我们自动创建不同策略配置的线程池,供我们直接使用。

    newCachedThreadPool

coreSize 线程数0,最大线程数无限制,线程的允许空闲时间是60s,阻塞队列是 SynchronousQueue。适用于“短任务”情况。由于采用SynchronousQueue,每当提交一个任务,都会超过阻塞队列的长度,导致创建新线程处理,所以说: 每当提交一个任务,都会创建一个线程,可能造成OOM。 此外,线程空闲1分钟就会销毁,所以该线程池可能会频繁地创建和销毁线程。

    newFixedThreadPool

coreSize 和最大线程数都是用户输入的,阻塞队列用的 LinkedBlockingQueue,线程的允许空闲时间是0s。其核心特性就是线程数不会增加,不会减少,线程池也不会自己销毁。由于阻塞队列是无限大的,不会执行拒绝策略。所以可能会堆积无限的请求,导致OOM。

    newSingleThreadExecutor

相当于线程数为1的 newFixedThreadPool,缺点和 newFixedThreadPool 一样。有的小伙伴可能会问,那它和单个线程有什么区别?

| newSingleThreadExecutor | Thread |

| — | — |

|

任务执行完成后,不会自动销毁,可以复用

|

任务执行完成后,会自动销毁

|

|

可以将任务存储在阻塞队列中,逐个执行

|

无法存储任务,只能执行一个任务

|

    newScheduledThreadPool

支持定时及周期性任务执行,需要注意的是,如果任务执行过程中抛出了异常就会停止执行任务,而且也不会再周期地执行该任务了。所以如果想保持任务周期执行,需要 catch 一切可能的异常。

    newWorkStealingPool

采用的 ForkJoin 框架,可以将任务进行分割,同时线程之间会互相帮助。另外,阻塞队列采用的 LinkedBlockingDeque,可以进行任务窃取。由于实际使用不多,这里只作了解。

实际使用时并不推荐这样去直接创建使用,阿里Java开发规约里面也有相应约束:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值