Java线程池面试题及答案(9道常见必考题)

Java线程池面试题及答案(9道常见必考题)-mikechen

Java面试经常问到Java并发编程里的线程池,今天给大家总结线程池面试题及答案@mikechen

什么是线程池?为什么要用线程池?

线程池里面存放了若干数量的线程,这些线程给我们程序去使用,使用的时候,就去线程池里面取一个,用完了再还回来,而不再是自我销毁。

线程池带来的好处:

  • 降低资源消耗
  • 提高相应速度
  • 提高线程的可管理型

线程池有哪7个核心参数?

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue < Runnable > workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

Java线程池面试题及答案(9道常见必考题)-mikechen

  • corePoolSize:核心线程数。也就是最少的线程数,即使空闲也要维护的线程数。
  • maximumPoolSize:池里最大线程数量。
  • keepAliveTime:线程最大活跃时间
  • unit:时间单位
  • workQueue:阻塞队列。线程用完时,任务放进的队列。
  • threadFactory:线程工厂
  • handler:丢弃策略。当队列也满的时候,如何处理的策略。

讲讲线程池工作原理 ?

池化技术主要是为了减少线程创建的时间,将可以复用的线程放在池里随时使用。

1、线程池刚创建时候,没有线程。

2、线程池指定任务execute时:

【初始执行阶段】

  • 如果线程池线程数量 < corePoolSize,会立刻创建线程并执行任务
  • 如果线程池线程数量 > corePoolSize,会把任务放入阻塞队列中

【任务队列满】

  • 如果阻塞队列任务已满,且正在运行的线程数< maximumPoolSize,继续创建非核心线程执行任务
  • 如果阻塞队列任务已满,且正在运行的线程数 = maximumPoolSize,线程池会抛出RejectExecutionException由handler处理

【线程空闲】

  • 线程如果空闲超过keepAliveTime,而且正在运行的线程数 > corePoolSize,该线程会被销毁。

如何设置合适的线程数量?

线程执行任务,主要是耗费的资源是CPU,所以:

  • IO密集型:等待I/O的时间很长,线程通常处于等待状态,CPU没有被充分使用,可以多设置一点,通常设置为:2*CPU核心数+1
  • CPU密集型:对CPU消耗大,线程太多的话线程切换反而浪费CPU时间片,通常设置为:CPU核心数+1

线程池拒绝策略有哪些?

Java线程池面试题及答案(9道常见必考题)-mikechen

  • AbortPolicy:直接抛出异常,阻止线程正常运行
  • CallerRunsPolicy:谁调用的execute,谁来执行(让父线程去执行)
  • DiscardOldestPolicy:丢弃当前最老的任务,然后重新尝试执行execute
  • DiscardPolicy:直接丢弃当前任务

 

谈谈任务队列?

任务队列只是先进先出队列吗? -> 如果任务有优先级怎么办? -> 知道优先级反转吗? -> 怎么解决优先级反转?

1、默认先进先出,可以用一些特殊的Queue,实现先进后出,例如LinkedBlockingDeque。

2、如果任务有优先级,更可以带权重的Queue,比如PriorityBlockingQueue。PriorityBlockingQueue是一个无界队列,利用小顶堆,把任务按照权重排序,出队一定是权重最大的。

3、优先级反转:线程A、B、C都需要执行任务,优先级A>B>C,其中A和C都去争抢一个资源,当低优先级线程C争取到了资源后,高优先级线程A即使拿到了CPU,也不能执行,从就绪状态变为等待资源的挂起状态。由于B处于就绪,A处于挂起(要等下一个调度才就绪),所以B明明优先级比A低,而且也没有资源竞争,却优先于A执行了。

优先级反转会导致任务执行顺序混乱。

4、解决方案:

核心就是,让C释放资源之前,B都不要去执行,那就提升C的优先级即可。

(1)最高优先级:低优先级任务C拿到资源时,将优先级提升至最大,释放后改回来。修改优先级会浪费CPU时间,因为不是每个资源都是多个线程抢的。

(2)优先级继承:高优先级任务A发现资源被低优先级任务C抢占,那么将低优先级任务C提升到和自己同等优先级。过程比较复杂,需要操作系统支持同等优先级。

(3)结合:高优先级任务A发现资源被低优先级任务C抢占,那么将低优先级任务C提升到最高优先级。

知道哪些常用的线程池?

1、ThreadpoolExecutor.newCachedThreadPool:线程池无限大,当执行上一个任务已经完成,会复用上一个任务的线程,而不是每次都新建线程。适合执行时间短的任务。

Java线程池面试题及答案(9道常见必考题)-mikechen

2、ThreadpoolExecutor.newSingleThreadExecutor:单线程运行的线程池。适合需要顺序执行的任务。

Java线程池面试题及答案(9道常见必考题)-mikechen

3、ThreadpoolExecutor.newFixedThreadPool:固定线程数量的线程池

Java线程池面试题及答案(9道常见必考题)-mikechen

4、ThreadpoolExecutor.newScheduleThreadPool:定时线程池。适合周期性执行的任务。

Java线程池面试题及答案(9道常见必考题)-mikechen

5、Executors.newWorkStealingPool:并发线程池(JDK 1.8)动态调整线程数,并且使用多个队列,以支持给定的并发级别。不保证任务执行顺序。

Java线程池面试题及答案(9道常见必考题)-mikechen

谈谈线程池里的线程?

线程是怎么回收空闲线程的?空闲的核心线程如何做到不被回收的?核心线程一定不能被回收吗?

1、不要回答有定时任务回收空闲线程,大No特No。

其实线程对象会被包装成worker,然后在循环遍历线程池任务队列时,超过指定时间获取不到任务,就开始移除worker,最后这些移除的worker会被GC从而回收线程。注意这里移除的是非核心线程。

2、有一个HashSet持有全部worker的引用,因此worker不会被回收,thread也就不会被回收。

3、当然不是,可以被回收,如果设置了allowCoreThreadTimeOut=true的话。

如果让你设计一个线程池,你如何设计?

至少需要4个结构:

1、线程池管理:Manger。处理线程生命周期,执行任务。

2、工作线程:Worker。执行具体的任务。

3、任务:Task。任务接口,供业务实现

4、任务队列:Queue。暂存没有资源执行的任务。

作者简介

陈睿|mikechen,10年+大厂架构经验,BAT资深面试官,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

👇阅读更多mikechen架构文章👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

评论交流
    说说你的看法