美团面试线程池追命5连问被问懵了,面试官让我回家等通知


近段时间,有小伙伴面试美团,说遇到了线程池的面试题,追命5连问,被问懵了。



第1问:线程池执行任务的具体流程是怎样的?

ThreadPoolExecutor中提供了两种执行任务的方法:

  1. void execute(Runnable command)

  2. Future submit(Runnable task)

实际上submit中最终还是调用的execute()方法,只不过会返回一个Future对象,用来获取任务执行结果:

public Future submit(Runnable task) {    if (task == nullthrow 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


这五种状态并不能任意转换,只会有以下几种转换情况:

  1. RUNNING -> SHUTDOWN:手动调用shutdown()触发,或者线程池对象GC时会调用finalize()从而调用shutdown()

  2. (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()触发,如果先调shutdown()紧着调shutdownNow(),就会发生SHUTDOWN -> STOP

  3. SHUTDOWN -> TIDYING:队列为空并且线程池中没有线程时自动转换

  4. STOP -> TIDYING:线程池中没有线程时自动转换(队列中可能还有任务)

  5. 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()方法中会额外再新增一个线程,这样就能维持住固定的核心线程数。


线程池基础:
从0开始深入理解Java线程池——ThreadPoolExecutor实战



请使用浏览器的分享功能分享到微信等