线程任务执行报错后,线程会不会挂掉,Java线程池
文章目录1. 情况一使用 execute() 提交任务 —— 线程会挂掉并创建一个新线程替代它️ 底层源码是怎么跑的2. 情况二使用 submit() 提交任务 —— 线程不会死但异常会被“吞掉”️ 为什么 submit() 能保住线程的命3. 面试官追问线程“死”和“不死”有什么影响哪种更好 总结你在面试时的完美答题范式这是一个非常经典的底层源码级面试题。你在深圳晞星智能科技一面时也正好被问到了这个问题 。当时面试官追问你“运行这个任务的线程会死掉吗”答案是这取决于你是通过execute()还是submit()来提交任务的。这两种提交方式会导致完全不同的底层处理机制结果一个会“死”一个会“活”。1. 情况一使用execute()提交任务 —— 线程会挂掉并创建一个新线程替代它如果你使用execute(Runnable command)提交任务并且任务在运行期间抛出了未捕获的异常RuntimeException线程会不会死会死。该工作线程Worker会因为异常而直接终止、退出生命周期。线程池会崩溃吗不会。线程池在底层捕获到了这个异常退出它会默默地把这个死掉的线程从线程池里移除然后重新创建一个全新的工作线程Worker补上空位维持核心线程数的平衡。日志现象异常堆栈信息会直接打印到你的控制台或标准错误日志Stderr中你不需要主动去写try...catch也能看到报错 。️ 底层源码是怎么跑的在ThreadPoolExecutor的runWorker(Worker w)方法中底层代码大体是这样实现的try{while(task!null||(taskgetTask())!null){beforeExecute(wt,task);try{task.run();// 1. 这里如果抛出 RuntimeException会直接向上抛出}catch(Throwablex){thrownx;throwx;// 2. 扔给外层}}}finally{// 3. 线程一旦异常退出一定会进到这里processWorkerExit(w,completedAbruptly);}在processWorkerExit工人退出处理方法中有一行核心代码// 如果是异常退出的completedAbruptly true底层会直接调用 addWorker(null, false);// 这意味着旧线程死了线程池立马原地新开一个线程作为替代品if(completedAbruptly)addWorker(null,false);2. 情况二使用submit()提交任务 —— 线程不会死但异常会被“吞掉”如果你是用submit(CallableT task)提交的任务线程会不会死绝对不会死。线程会完好无损地活下来并且回到线程池中等待执行下一个任务。异常去哪了异常被线程池“吞”掉了。如果你不做特殊处理控制台和日志文件里是一片风平浪静什么报错都不会打印。怎么拿到报错submit()方法会返回一个Future对象。只有当你调用future.get()去获取结果时之前执行时发生的异常才会以ExecutionException的形式重新抛出来。️ 为什么submit()能保住线程的命因为submit()并没有直接把你的Runnable/Callable扔给线程运行而是偷偷在外面包裹了一层FutureTask。在FutureTask.run()的底层源码里它自己把整个异常给try...catch住了publicvoidrun(){try{CallableVccallable;if(c!nullstateNEW){Vresultc.call();set(result);// 成功}}catch(Throwableex){// 关键点发生异常不往外抛而是把异常对象赋值给内部变量 outcomesetException(ex);}}对于工作线程来说它只是成功执行完了FutureTask的run()方法根本没有感知到内部业务报错所以线程完全不会死。3. 面试官追问线程“死”和“不死”有什么影响哪种更好既然execute()会让线程死掉、重新建线程而submit()能保住线程那是不是submit()性能更好创建线程的开销execute()导致工作线程频繁死掉和重建是有性能损耗的。ThreadLocal 内存泄露问题如果你在线程中使用了ThreadLocal忘记清理在submit()模式下线程不死这个ThreadLocal就会一直常驻内存引发严重的内存泄露而在execute()异常死掉的模式下线程由于直接退出了它携带的ThreadLocal变量也会随着线程消亡而被垃圾回收JVM 会回收 Thread 对象的threadLocals映射表。这也是一个有趣的硬币两面性。 总结你在面试时的完美答题范式下次如果再遇到这个问题你可以这样闭环回答“这取决于任务的提交方式。如果是用execute()提交当任务抛出未捕获的 RuntimeException 时该工作线程会直接终止并消亡。但线程池本身不会崩它会在finally块的processWorkerExit方法中把死掉的线程移除并自动创建一个新线程补上此时错误日志会自动打印在控制台 。如果是用submit()提交底层会将任务封装成FutureTask。其内部的run方法会主动用try...catch吞掉异常并暂存到outcome变量中。因此运行线程不会死它会安全回到线程池。只有当我们调用future.get()时异常才会被重新抛出。”这样回答从结论、底层源码、再到两种机制的对比完美通关