并发编程(三):线程池基本面试题(必背题目)

目录

一、背景

二、高频面试题(八股文)

1、什么是线程池?线程池有什么好处(被问概率很小)?

2、有几种常见的线程池(必知必会)?

3、线程池的主要参数有哪些(必知必会)?

4、线程池的工作流程(必知必会)?

5、线程池的拒绝策略有哪些(必知必会)?

6、线程池有哪几种工作队列(被问概率:小于10%)?

7、如何合理设置线程池的核心线程数(必知必会)?

8、线程池异常是怎么处理的(被问概率很小)?

9、线程池优化了解吗(40%可能性被问到)?

10、你能设计实现一个线程池吗(BAT容易问到,小公司不会)?

三、参考文献


一、背景

金三银四话说是跳槽的最佳时机,各大公司也在此时招兵买马,当然面试者也很多,基本上一个面试官至少2场面试,而面试者也旗鼓相当,每天面2家的节奏。我本人也参加了一些公司的面试,发现线程池被问到的概率达到50%以上,而且问的问题大概就那几个,没啥新意。唯一有新意的是一个架构师问我“如何设计一个线程池”,虽然看过,但是没有理解,忘了!!!好记性不如烂笔头,还是记录下吧。

二、高频面试题(八股文)

1、什么是线程池?线程池有什么好处(被问概率很小)?

所谓线程池,通俗来讲,就是一个管理线程的池子。它可以容纳多个线程,其中的线程可以反复利用,省去了频繁创建线程对象的操作。
好处:
1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。


2、有几种常见的线程池(必知必会)?

1)定长线程池(FixedThreadPool)
2)定时线程池(ScheduledThreadPool)
3)可缓存线程池(CachedThreadPool)
4)单线程化线程池(SingleThreadExecutor)

核心概念:这四个线程池的本质都是ThreadPoolExecutor对象(自己看源码)
不同点在于:
1)FixedThreadPool:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
2)ScheduledThreadPool:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。
3)CachedThreadPool:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
4)SingleThreadExecutor:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列


上面只是面试题目,除了面试之外想了解细节,看看这个文章: https://blog.csdn.net/u013541140/article/details/95225769

3、线程池的主要参数有哪些(必知必会)?

1)corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
2)maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
3)keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
4)unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
5)workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
6)threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
7)handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。

4、线程池的工作流程(必知必会)?

这个问题回答的时候,最好用讲故事的方式进行。
假如核心线程数是5,最大线程数是10,阻塞队列也是10
1)有新任务来的时候,将先使用核心线程执行;
2)当任务数达到5个的时候,第6个任务开始排队;
3)当任务数达到15个的时候,第16个任务将开启新的线程执行,也就是第6个线程
4)当任务数达到20个的时候,线程池满了,如果有第21个任务,将执行拒绝策略(见下一个问题)
流程图:

5、线程池的拒绝策略有哪些(必知必会)?

1)AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。
2)CallerRunsPolicy:由调用线程处理该任务。
3)DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
4)DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

6、线程池有哪几种工作队列(被问概率:小于10%)?

1)ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。
2)LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为 Integer.MAX_VALUE。
3)PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
4)DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用 put() 方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
5)LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。
6)LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 7)SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为一致,但是是无界的阻塞队列。

7、如何合理设置线程池的核心线程数(必知必会)?

 线程数量的计算公式一般都是 线程数=Ncpu(1+w/e).其中W代表的是阻塞耗时,e代表的是计算耗时。
1)IO密集型:如果存在IO,那么W/e肯定大于1,但是需要考虑系统内存上限(没开启一个线程都需要内存空间),这个需要服务器测试到底多少个线程比较合适(CPU占比,线程数、总耗时、内存耗时)。保守取值为1,及线程数=2Ncpu+1,
2)计算密集型:假设没有等待时间,则W=0,W/C=0,线程数= Ncpu+1. 其中多出来的一个是为了防止线程偶发的缺页中断。
服务性能I0优化有一个估算公式:
最佳线程数目=((线程等待时间+线程CPU时间)/线程CPU时间)X CPU数量
比如平均每个线程CPU运行时间为0.5s,而线程等待时间为1.5s(比如IO),CPU个数为8.则根据以上公式可以估算((1.5+0.5)/0.5)X 8=32
公式进一步转化:
最佳线程数目 = (线程等待时间/线程CPU时间+1)X 线程数
参考文献:
https://www.cnblogs.com/loveLands/articles/10016170.html

8、线程池异常是怎么处理的(被问概率很小)?

由于几乎不被问到,所以不总结了,直接看这两篇文章吧:

1)一个线程池中的线程异常了,那么线程池会怎么处理这个线程?
https://www.cnblogs.com/fanguangdexiaoyuer/p/12332082.html
2)【线程池】线程池的线程遇到异常后去哪里?怎么处理?
https://blog.csdn.net/qfzhangwei/article/details/105181055

9、线程池优化了解吗(40%可能性被问到)?

这个问题和第7个问题很类似,可以参考回答。其他答案:
1)用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM
2)如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务
3)最大线程数一般设为2N+1最好,N是CPU核数
4)核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数
5)如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果。

10、你能设计实现一个线程池吗(BAT容易问到,小公司不会)?

可以这样作答:
我们自己的实现就是完成这个核心流程:
1)线程池中有N个工作线程
2)把任务提交给线程池运行
3)如果线程池已满,把任务放入队列
4)最后当有空闲时,获取队列中任务来执行
具体的分析可以参考这篇文献:
https://blog.csdn.net/dgutliangxuan/article/details/103642963

其他:
如果你想了解更多,建议阅读参考文献4(面渣逆袭:线程池夺命连环十八问),这篇文章总结的还是不错的。

三、参考文献

1、什么是线程池,线程池的作用
https://blog.csdn.net/weixin_50897975/article/details/119947495
2、Java 多线程:彻底搞懂线程池
https://blog.csdn.net/u013541140/article/details/95225769
3、线程池中如何确定线程的数目
https://www.cnblogs.com/loveLands/articles/10016170.html
4、面渣逆袭:线程池夺命连环十八问
https://blog.csdn.net/sinat_40770656/article/details/121370427
5、一个有趣的问题 : 如何设计一个线程池
https://blog.csdn.net/dgutliangxuan/article/details/103642963
 

你可能感兴趣的:(并发编程,并发编程,多线程)