经典面试题:线程池提交任务,发生异常,你是怎么处理的?

发布于 2021-09-07 11:34 ,所属分类:2021面试经验技巧分享

前言

大家好,我是程序员田螺,今天我们来看一道特别经典的面试题:线程池中的线程,提交新任务,发生异常时,你是如何处理的?

1.当提交新任务时,异常如何处理呢?

我们先来看一段代码:

ExecutorServicethreadPool=Executors.newFixedThreadPool(5);
for(inti=0;i<5;i++){
threadPool.submit(()->{
System.out.println("currentthreadname"+Thread.currentThread().getName());
Objectobject=null;
System.out.print("result##"+object.toString());
});
}

显然,这段代码会有异常,我们再来看看运行结果

虽然没有结果输出,但是也没有抛出异常,所以我们无法感知任务出现了异常,所以需要添加try/catch。如下图:

因此,线程的异常处理,我们可以直接try...catch捕获。

2. 线程池exec.submit()的执行流程

通过debug上面有异常的submit方法(建议大家也去debug看一下,下图的每个方法内部是我打断点的地方),处理有异常submit方法的主要执行流程图如下:

submit方法执行流程
//构造feature对象
/**
*@throwsRejectedExecutionException{@inheritDoc}
*@throwsNullPointerException{@inheritDoc}
*/

publicFuture<?>submit(Runnabletask){
if(task==null)thrownewNullPointerException();
RunnableFuture<Void>ftask=newTaskFor(task,null);
execute(ftask);
returnftask;
}
protected<T>RunnableFuture<T>newTaskFor(Runnablerunnable,Tvalue){
returnnewFutureTask<T>(runnable,value);
}
publicFutureTask(Runnablerunnable,Vresult){
this.callable=Executors.callable(runnable,result);
this.state=NEW;//ensurevisibilityofcallable
}
publicstatic<T>Callable<T>callable(Runnabletask,Tresult){
if(task==null)
thrownewNullPointerException();
returnnewRunnableAdapter<T>(task,result);
}
//线程池执行
publicvoidexecute(Runnablecommand){
if(command==null)
thrownewNullPointerException();
intc=ctl.get();
if(workerCountOf(c)<corePoolSize){
if(addWorker(command,true))
return;
c=ctl.get();
}
if(isRunning(c)&&workQueue.offer(command)){
intrecheck=ctl.get();
if(!isRunning(recheck)&&remove(command))
reject(command);
elseif(workerCountOf(recheck)==0)
addWorker(null,false);
}
elseif(!addWorker(command,false))
reject(command);
}
//捕获异常
publicvoidrun(){
if(state!=NEW||
!UNSAFE.compareAndSwapObject(this,runnerOffset,
null,Thread.currentThread()))
return;
try{
Callable<V>c=callable;
if(c!=null&&state==NEW){
Vresult;
booleanran;
try{
result=c.call();
ran=true;
}catch(Throwableex){
result=null;
ran=false;
setException(ex);
}
if(ran)
set(result);
}
}finally{
//runnermustbenon-nulluntilstateissettledto
//preventconcurrentcallstorun()
runner=null;
//statemustbere-readafternullingrunnertoprevent
//leakedinterrupts
ints=state;
if(s>=INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}

通过以上分析,submit执行的任务,可以通过Future对象的get方法接收抛出的异常,再进行处理。我们再通过一个demo,看一下Future对象的get方法处理异常的姿势,如下图:

因此,可以使用这两种方案处理线程池异常:

  • 1.在任务代码try/catch捕获异常,
  • 2.通过Future对象的get方法接收抛出的异常

3. 为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常

也可以为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常,我们直接看这样实现的正确姿势:

ExecutorServicethreadPool=Executors.newFixedThreadPool(1,r->{
Threadt=newThread(r);
t.setUncaughtExceptionHandler(
(t1,e)->{
System.out.println(t1.getName()+"线程抛出的异常"+e);
});
returnt;
});
threadPool.execute(()->{
Objectobject=null;
System.out.print("result##"+object.toString());
});

运行结果:

4.重写ThreadPoolExecutor的afterExecute方法,处理传递的异常引用

这是jdk文档的一个demo:

classExtendedExecutorextendsThreadPoolExecutor{
//这可是jdk文档里面给的例子。。
protectedvoidafterExecute(Runnabler,Throwablet){
super.afterExecute(r,t);
if(t==null&&rinstanceofFuture<?>){
try{
Objectresult=((Future<?>)r).get();
}catch(CancellationExceptionce){
t=ce;
}catch(ExecutionExceptionee){
t=ee.getCause();
}catch(InterruptedExceptionie){
Thread.currentThread().interrupt();//ignore/reset
}
}
if(t!=null)
System.out.println(t);
}
}}

5. 因此,被问到线程池异常处理,如何回答?

--end--

求点赞、在看、转发,感谢

相关资源