近段时间,有小伙伴面试美团,说遇到了线程池的面试题,追命5连问,被问懵了。
第1问:线程池执行任务的具体流程是怎样的?
ThreadPoolExecutor中提供了两种执行任务的方法:
void execute(Runnable command)
Future submit(Runnable task)
实际上submit中最终还是调用的execute()方法,只不过会返回一个Future对象,用来获取任务执行结果:
近段时间,有小伙伴面试美团,说遇到了线程池的面试题,追命5连问,被问懵了。
第1问:线程池执行任务的具体流程是怎样的?
ThreadPoolExecutor中提供了两种执行任务的方法:
void execute(Runnable command)
Future submit(Runnable task)
实际上submit中最终还是调用的execute()方法,只不过会返回一个Future对象,用来获取任务执行结果:
public Future> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);execute(ftask);return ftask;}
execute(Runnable command)方法执行时会分为三步:

注意:提交一个Runnable时,不管当前线程池中的线程是否空闲,只要数量小于核心线程数就会创建新线程。
注意:ThreadPoolExecutor相当于是非公平的,比如队列满了之后提交的Runnable可能会比正在排队的Runnable先执行。
第2问:线程池的五种状态是如何流转的?
线程池有五种状态:
RUNNING:会接收新任务并且会处理队列中的任务
SHUTDOWN:不会接收新任务并且会处理队列中的任务
STOP:不会接收新任务并且不会处理队列中的任务,并且会中断在处理的任务(注意:一个任务能不能被中断得看任务本身)
TIDYING:所有任务都终止了,线程池中也没有线程了,这样线程池的状态就会转为TIDYING,一旦达到此状态,就会调用线程池的terminated()
TERMINATED:terminated()执行完之后就会转变为TERMINATED
这五种状态并不能任意转换,只会有以下几种转换情况:
RUNNING -> SHUTDOWN:手动调用shutdown()触发,或者线程池对象GC时会调用finalize()从而调用shutdown()
(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()触发,如果先调shutdown()紧着调shutdownNow(),就会发生SHUTDOWN -> STOP
SHUTDOWN -> TIDYING:队列为空并且线程池中没有线程时自动转换
STOP -> TIDYING:线程池中没有线程时自动转换(队列中可能还有任务)
TIDYING -> TERMINATED:terminated()执行完后就会自动转换

第3问:线程池中的线程是如何关闭的?
我们一般会使用thread.start()方法来开启一个线程,那如何停掉一个线程呢?
线程池中就是利用线程中断机制来停止线程的。比如shutdownNow()方法中会调用:
void interruptIfStarted() {Thread t;if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {try {t.interrupt();} catch (SecurityException ignore) {}}}
第4问:线程池为什么一定得是阻塞队列?
线程池中的线程在运行过程中,执行完创建线程时绑定的第一个任务后,就会不断的从队列中获取任务并执行,那么如果队列中没有任务了,线程为了不自然消亡,就会阻塞在获取队列任务时,等着队列中有任务过来就会拿到任务从而去执行任务。
通过这种方法能最终确保,线程池中能保留指定个数的核心线程数,关键代码为:
try {Runnable r = timed ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :workQueue.take();if (r != null)return r;timedOut = true;} catch (InterruptedException retry) {timedOut = false;}
某个线程在从队列获取任务时,会判断是否使用超时阻塞获取,我们可以认为非核心线程会poll(),核心线程会take(),非核心线程超过时间还没获取到任务后面就会自然消亡了。
第5问:线程发生异常,会被移出线程池吗?
答案是会的,那有没有可能核心线程数在执行任务时都出错了,导致所有核心线程都被移出了线程池?

在源码中,当执行任务时出现异常时,最终会执行processWorkerExit(),执行完这个方法后,当前线程也就自然消亡了,但是!processWorkerExit()方法中会额外再新增一个线程,这样就能维持住固定的核心线程数。