Executors.newFixedThreadPool藏着坑(无界队列→OOM)。中高级Java面试就要你手撸线程池、每个参数给出理由。
七个参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
提交流程:(1)工作线程<core→开新worker;(2)core满→入队;(3)队列满→再开worker直到max;(4)max也满→走拒绝策略。
队列很关键:LinkedBlockingQueue(默认无界,永远卡在第2步——OOM隐患)、ArrayBlockingQueue(有界)、SynchronousQueue(不缓冲,直接交接)。
四种内置拒绝:AbortPolicy(抛异常)、CallerRunsPolicy(提交线程自己跑——天然反压)、DiscardPolicy(偷偷丢)、DiscardOldestPolicy。
配比:CPU密集≈N_cpu+1;IO密集≈N_cpu×(1+等待/计算)。生产必监控队列深度和活跃线程数。
我从不用Executors.newFixedThreadPool——它用无界LinkedBlockingQueue,maxPoolSize变摆设,内存涨到OOM为止。我都是new ThreadPoolExecutor自己建。Web服务器一般SynchronousQueue+大maxPoolSize+CallerRunsPolicy——过载时反压给提交方,不会让任务堆着。ThreadFactory一定起带业务含义的名字,线上排查堆栈和日志时看得出线程来自哪个池,太重要了。上线前必接监控:getQueue().size()和getActiveCount(),看着波形调参数。
Tomcat有个改造过的TaskQueue把顺序翻了——先扩线程到max再入队,适合web场景。
不可控环境别用Executors.newCachedThreadPool——max是Integer.MAX_VALUE。
即答侠会让你口述"提交流程四分支"——面试时一口气说出来不卡壳,靠反复练。
默认不会。allowCoreThreadTimeOut(true)之后core线程也会在keepAliveTime后销毁。
未捕获异常会把worker干掉,池子会补新worker。最佳实践:要么任务内try/catch,要么用submit()返回Future然后检查Future.get()。