当前位置: 首页 > 后端技术 > Java

面试官:线程池执行过程中遇到异常怎么办,如何处理?

时间:2023-04-02 01:44:49 Java

很容易理解,线程遇到未处理的异常就结束了。当一个线程遇到未捕获的异常,就无法继续执行,留给它的就是垃圾回收。线程池中的线程频繁出现未捕获的异常当线程池中的线程频繁出现未捕获的异常时,线程的复用率大大降低,需要不断地创建新的线程。做一个实验:publicclassThreadExecutor{privateThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(1,1,60,TimeUnit.SECONDS,newArrayBlockingQueue<>(200),newThreadFactoryBuilder().setNameFormat("customThread%d").build());@Testpublicvoidtest(){IntStream.rangeClosed(1,5).forEach(i->{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}threadPoolExecutor.execute(()->{intj=1/0;});});}}新建一个只有一个线程的线程池,每0.1s提交一个任务,任务为1/0计算。线程“customThread0”中的异常java.lang.ArithmeticException:/在thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)处为零java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread1"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)在java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617))在java.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread2"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)在java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread3"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)在java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)在java.lang.Thread.run(Thread.java:748)线程“customThread4”中的异常java.lang.ArithmeticException:/在thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor处为零。runWorker(ThreadPoolExecutor.java:1142)在java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)在java.lang.Thread.run(Thread.java:748)线程“customThread5”java.lang.ArithmeticException:/在thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)在java.util.concurrent.ThreadPoolExecutor.r处为零unWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)可以看出执行的线程每次都不一样,之前的线程之所以没有被重用,是因为一个未捕获的异常。让我们尝试捕获异常:publicclassThreadExecutor{privateThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(1,1,60,TimeUnit.SECONDS,newArrayBlockingQueue<>(200),newThreadFactoryBuilder().setNameFormat("customThread%d")。建造());@Testpublicvoidtest(){IntStream.范围关闭(1、5)。forEach(i->{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}threadPoolExecutor.execute(()->{try{intj=1/0;}catch(异常e){System.out.println(Thread.currentThread().getName()+""+e.getMessage());}});});}}customThread0/byzerocustomThread0/byzerocustomThread0/byzerocustomThread0/byzerocustomThread0/byzero可以看出,当异常被捕获时,线程可以被复用。问题是我们不可能捕获代码中的所有异常。如果要捕获那些业务代码没有捕获到的异常,可以设置Thread类的uncaughtExceptionHandler属性。这时候使用ThreadFactoryBuilder就比较方便了。ThreadFactoryBuilder是guava提供的ThreadFactory生成器。newThreadFactoryBuilder().setNameFormat("customThread%d").setUncaughtExceptionHandler((t,e)->System.out.println(t.getName()+"发生异常"+e.getCause())).build()修改后:publicclassThreadExecutor{privatestaticThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(1,1,60,TimeUnit.SECONDS,newArrayBlockingQueue<>(200),newThreadFactoryBuilder().setNameFormat("customThread%d").settionUncaughtExcepler((t,e)->System.out.println("UncaughtExceptionHandlercaught:"+t.getName()+"发生异常"+e.getMessage())).build());@Testpublicvoidtest(){IntStream.rangeClosed(1,5).forEach(i->{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}threadPoolExecutor.execute(()->{System.out.println("Thread"+Thread.currentThread().getName()+"Execution");intj=1/0;});});}}线程customThread0ExecutionUncaughtExceptionHandlercaught:customThread0occurredexception/byzerothreadcustomThread1执行UncaughtExceptionHandler捕获:customThread1异常发生/零线程customThread2执行UncaughtExceptionHandler捕获:customThread2执行异常/零线程customThread3执行UncaughtExceptionHandler捕获:customThread3执行异常/零线程customThread4执行UncaughtExceptionHandler捕获到:customThread4异常/by0可见,结果不是我们想象的,线程池中原来的线程没有被重用!所以使用UncaughtExceptionHandler来吞掉异常,让线程重用似乎行不通。它只是做了一层异常保证处理。将执行更改提交尝试publicclassThreadExecutor{privatestaticThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(1,1,60,TimeUnit.SECONDS,newArrayBlockingQueue<>(200),newThreadFactoryBuilder().setNameFormat("customThread%d").setUncaughtExceptionHandler((t,e)->System.out.println("UncaughtExceptionHandler捕获到:"+t.getName()+"发生异常"+e.getMessage())).build());@Testpublicvoidtest(){IntStream.rangeClosed(1,5).forEach(i->{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}Futurefuture=threadPoolExecutor.submit(()->{System.out.println("线程"+Thread.currentThread().getName()+"执行");intj=1/0;});try{future.get();}catch(InterruptedExceptione){e.printStackTrace();}catch(ExecutionExceptione){e.printStackTrace();}});}}线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/零线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/零线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/零线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/零线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/零线程通过submit可以屏蔽线程异常只有在get()的执行结果达到线程复用时才会抛出。原因是通过submit提交的线程在发生异常时会保存异常,直到future.get();才会抛出;这是Futuretask的run()方法的一部分,参见setException:publicvoidrun(){try{Callablec=callable;if(c!=null&&state==NEW){V结果;布尔值;尝试{结果=c.call();跑=真;}catch(Throwableex){结果=null;跑=假;设置异常(前);}如果(运行)设置(结果);}}}protectedvoidsetException(Throwablet){if(UNSAFE.compareAndSwapInt(this,stateOffset,NEW,COMPLETING)){outcome=t;UNSAFE.putOrderedInt(this,stateOffset,EXCEPTIONAL);//最终状态finishCompletion();}}将异常存储在outcome对象中,不抛出,看get方法:publicVget()throwsInterruptedException,ExecutionException{ints=state;如果(s<=完成)s=awaitDone(false,0L);返回报告;}私有V报告(ints)抛出ExecutionExceptionion{对象x=结果;如果(s==NORMAL)返回(V)x;如果(s>=CANCELLED)抛出新的CancellationException();thrownewExecutionException((Throwable)x);}仅在结果异常时抛出总结1.尝试手动捕获线程池中线程中的异常2.通过设置ThreadFactory的UncaughtExceptionHandler,可以处理未捕获的异常底端。通过execute提交任务,线程仍然会被中断,通过submit提交任务,可以得到线程的执行结果,得到执行结果会抛出线程异常。本文链接:https://blog.csdn.net/weixin_...版权声明:本文为博主原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本转载声明。近期热点文章推荐:1.1,000+Java面试题及答案(2021最新版)2.别在满屏的if/else中,试试策略模式,真的很好吃!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!