简介
整理一些面试时线程池常问的问题
1.核心线程如何一直保活
答案:由于任务都是存在阻塞队列中,Worker从队列拿任务时通过阻塞队列take()方法让线程等待,使得Worker的run()方法一直阻塞,直到获取到执行任务,执行完任务后继续阻塞等待,使得线程生命周期一直在RUNNABLE和WAITING状态之间流转,保证核心线程一直存活。
2.非核心线程何时死亡?
还是刚才的代码:
答案:非核心线程延迟死亡的条件为当前线程数大于最大核心线程数,并且也获取不到任务。
3.核心线程何时死亡?
还是刚才的代码:
allowCoreThreadTimeOut是ThreadPoolExecutor类的成员属性,只要设置这个变量,在执行keepAliveTime还未获取到执行任务时就会移出线程池。
4.线程池如何保证并发安全
ThreadPoolExecutor中使用HashSet用于存放Worker,当添加一个线程时:
对于workers集合的操作都是采用ReentrantLock锁来保证的,同一时间只有一个线程可以操作集合对象
5.线程数设置多少合适
对于 CPU 密集型计算,多线程本质上是提升多核 CPU 的利用率,所以对于一个 4 核的 CPU,每个核一个线程,理论上创建 4 个线程就可以了,再多创建线程也只是增加线程切换的成本。所以,对于 CPU 密集型的计算场景,理论上“线程的数量 =CPU 核数”就是最合适的。不过在工程上,线程的数量一般会设置为“CPU 核数 +1”,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证 CPU 的利用率;
对于 I/O 密集型的计算场景,如果 CPU 计算和 I/O 操作的耗时是1:1,那么 2 个线程是最合适的。如果 CPU 计算和 I/O 操作的耗时是 1:2,那多少个线程合适呢?是 3 个线程。
例如:CPU 在 A、B、C 三个线程之间切换,对于线程 A,当 CPU 从B、C 切换回来时,线程 A 正好执行完 I/O 操作。这样 CPU 和 I/O 设备的利用率都达到了100%;