问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

线程池执行过程中遇到异常会发生什么?怎样处理?

发布网友 发布时间:2024-09-25 16:52

我来回答

1个回答

热心网友 时间:2024-10-16 21:15

线程遇到未处理的异常就结束了

这个好理解,当线程出现未捕获异常的时候就执行不下去了,留给它的就是垃圾回收了。

线程池中线程频繁出现未捕获异常

当线程池中线程频繁出现未捕获的异常,那线程的复用率就大大降低了,需要不断地创建新线程。

做个实验:

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的计算。

Exceptioninthread"customThread0"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread1"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread2"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.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)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread4"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread5"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(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").build());@Testpublicvoidtest(){IntStream.rangeClosed(1,5).forEach(i->{try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}threadPoolExecutor.execute(()->{try{intj=1/0;}catch(Exceptione){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").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();}threadPoolExecutor.execute(()->{System.out.println("线程"+Thread.currentThread().getName()+"执行");intj=1/0;});});}}线程customThread0执行UncaughtExceptionHandler捕获到:customThread0发生异常/byzero线程customThread1执行UncaughtExceptionHandler捕获到:customThread1发生异常/byzero线程customThread2执行UncaughtExceptionHandler捕获到:customThread2发生异常/byzero线程customThread3执行UncaughtExceptionHandler捕获到:customThread3发生异常/byzero线程customThread4执行UncaughtExceptionHandler捕获到:customThread4发生异常/byzero

可见,结果并不是我们想象的那样,线程池中原有的线程没有复用!所以通过UncaughtExceptionHandler想将异常吞掉使线程复用这招貌似行不通。它只是做了一层异常的保底处理。

将excute改成submit试试

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();}Future<?>future=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:/byzero线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/byzero线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/byzero线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/byzero线程customThread0执行java.util.concurrent.ExecutionException:java.lang.ArithmeticException:/byzero

通过submit提交线程可以屏蔽线程中产生的异常,达到线程复用。当get()执行结果时异常才会抛出。

原因是通过submit提交的线程,当发生异常时,会将异常保存,待future.get();时才会抛出。

这是Futuretask的部分run()方法,看setException:

publicvoidrun(){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);}}}protectedvoidsetException(Throwablet){if(UNSAFE.compareAndSwapInt(this,stateOffset,NEW,COMPLETING)){outcome=t;UNSAFE.putOrderedInt(this,stateOffset,EXCEPTIONAL);//finalstatefinishCompletion();}}

将异常存在outcome对象中,没有抛出,再看get方法:

Exceptioninthread"customThread0"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread1"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread2"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.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)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread4"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)Exceptioninthread"customThread5"java.lang.ArithmeticException:/byzeroatthread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)atjava.lang.Thread.run(Thread.java:748)0

当outcome是异常时才抛出。

总结

1、线程池中线程中异常尽量手动捕获

2、通过设置ThreadFactory的UncaughtExceptionHandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出。

原文链接:https://blog.csdn.net/weixin_37968613/article/details/108407774

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
小学生作文《我的辛福是什么》我要例文 昆明司法亲子鉴定中心在哪里?流程是怎么样的? XPS Document Writer只能在XPS中安装吗? ...Office2010后,没有Microsoft XPS Document Writer,请问安装时选择哪 ... 台州有哪些祥生楼盘 恒大新楼盘有哪些 空腹血糖和餐后两小时血糖正常值是多少 nova4怎么设置来电停机 谁知道一首英文歌曲,刚出来是小孩子唱的,然后是个男的唱的其中有一句... 有一首歌的歌词中有「就让往事随风都随风」它的歌名是啥啊???_... python最多创建多少个线程(2023年最新整理) 大航海时代4最强战舰排名 面试官:线程池多余的线程是如何回收的? 多达100是什么意思? as much as 与as many as 被讹诈的交通事故全责方有什么应对策略 交通事故全责方遭后期讹诈如何应对 认了全责后续有啥麻烦 认全责后期避免讹诈怎么做-公路资讯 禅宗道埸适合种什麼植物? 寺院能种月季吗? 寺院种什么品种的竹子 北京消费券在哪里领 京东消费券怎么领取?领取入口在哪里?有哪些城市?怎么用? 北京消费券怎么领_北京发放122亿元消费券领取方法 京东推出“北京绿色消费券”专区 买好物可领大额券 京东北京券非本地咋领 怎么领北京消费券啊? 七国的领土现在分布在哪些省? 战国时期七国分别在哪个省份 1平方的铜线能带多少千瓦? 大航海时代4里最厉害的战船是英国伦敦无敌战舰吗 深圳的自考本科流程是什么? 深圳市自考学分怎么算的? 从昆明自驾去西双版纳沿途有什么好玩的地方 爱你要留的繁体字怎么写? 爱你:-*静 的繁体字怎么写?? 夫妻经常吵架风水化解,夫妻总吵架的风水原因 夫妻总吵架是因为这风水 夫妻总吵架的风水原因 夫妻总吵架的风水原因 家里总吵架风水 夫妻吵架风水破解方法 谷歌浏览器怎么启动Flash插件? 怎样在谷歌浏览器中开启Flash插件? 她是雪地捡回的弃婴,爹娘收养她的目的,是替姐姐嫁做棋子,她该怎么... 追星的粉丝是什么到底什么是追星的粉丝 什么样的粉丝才是理智的追星族呢? 为什么粉丝总有一种“哥哥只有我们”的心态? 2023广州旅游必去十大景点排名 ...歌词里有朋友都说你(什么的) 他们不懂你的好 是一首甜蜜的歌... &lt;就是你甜蜜蜜得笑 害我的小鹿不停不停的乱跳&gt;这是什么歌曲?谁能告诉...