线程池七大地狱级问题,95%的同学都不会!

53 阅读4分钟

本文首发于公众号:托尼学长,立个写 1024 篇原创技术面试文章的flag,欢迎过来视察监督~

“在你项目中,如何对Kafka和RocketMQ选型的”,这是在Java技术面试中经常被问到的一个问题,也是比较坑的一个问题。

线程池七大地狱级问题,95%的同学都不会!

你以为现在面试官问线程池方面的问题,还只问什么线程池七大参数和线程池中线程数量配置多少吗?早就不是了!

下面我列举一下最近面试被问得最多的线程池七大地狱问题,并一一进行讲解。

1、线程池中的核心线程可以被回收掉吗?

可通过设置ThreadPoolExecutor.allowCoreThreadTimeOut(true)的方式,使其空闲时间超过keepAliveTime后被回收。

2、线程池中的核心线程预热是什么意思?

默认情况下,在创建了线程池后,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务。

除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,在没有任务到来之前就预创建corePoolSize个线程或者一个线程,这就是核心线程预热。

3、线程池状态包括哪些?

RUNNING:运行状态,线程池创建好之后就会进入此状态,可接收新任务并处理队列中任务。

SHUTDOWN:关闭状态,不再接受新任务提交,但是会将已保存在任务队列中的任务处理完。

STOP:停止状态,不再接受新任务提交,并且会中断当前任务并清空队列。

TIDYING:整理状态,所有任务(包括任务队列中的任务)都执行完毕后,当前线程池中的活动线程数降为 0 时的状态。到此状态之后,会调用线程池的 terminated() 方法。

TERMINATED:销毁状态,当执行完线程池的 terminated() 方法之后变为此状态,线程池完全被销毁。

4、如何判断线程池中的任务是否全部完成?

getCompletedTaskCount()和getTaskCount()是ThreadPoolExecutor中用于监控任务状态的两个方法,主要区别在于返回值的含义和用途。

前者返回已完成任务的数量,不包括正在执行或等待的任务,后者返回计划执行的总任务数,包括已完成、正在执行和等待的任务。

因此,判断任务是否全部完成,直接比较两者值是否相等。当然,前提是在确保无新任务提交的情况下。

5、为什么不建议CompletableFuture使用默认的ForkJoinPool?

CompletableFuture默认使用的线程池是ForkJoinPool.commonPool(),它是JVM全局共享的线程池,所有未显式指定线程池的CompletableFuture任务都会竞争同一池资源。

在高并发场景下,若部分任务出现IO操作、锁等待等阻塞现象时,会导致池内线程耗尽,其他依赖该线程池的任务被迫等待,拖累整体性能。

6、newSingleThreadExecutor(单线程线程池)的适用场景是什么?

newSingleThreadExecutor中的所有任务按提交顺序排队执行(FIFO 队列),且同一时间仅处理 1 个任务,本质是 “单线程 + 无界任务队列” 的组合。

其适用场景的核心原则是:需要 “串行执行、顺序可控、任务互不干扰”,且任务执行时间较短、无高并发压力的场景。

7、newFixedThreadPool和newCachedThreadPool的各自适用场景是什么?

newFixedThreadPool的核心特点是线程数量固定,使用无界队列(LinkedBlockingQueue)存储待执行任务,若线程都在忙,新任务会排队等待,且线程空闲时不会销毁,会一直保留以复用。

newCachedThreadPool的核心特点线程数量动态扩展:初始为 0,根据任务需求动态创建新线程,上限为 Integer.MAX_VALUE(理论无限制)。

其使用同步队列(SynchronousQueue),直接提交任务给线程执行,不排队(若没有空闲线程,则创建新线程)且线程空闲超过 60 秒会被自动销毁。

newFixedThreadPool适用于稳定的并发需求,需要长期维持一定数量的线程来处理任务的场景,如服务器接收固定数量的客户端连接,且为资源敏感型、需要控制并发度的任务。

newCachedThreadPool适用于短生命周期、高突发任务场景,比如:处理大量毫秒级短时 HTTP 请求,或是任务执行时间不稳定,部分任务可能很快完成,部分可能耗时较长,缓存线程池可灵活调整线程数的场景。

这七大地狱级线程池问题,你答对了几道?