资讯专栏INFORMATION COLUMN

任务异常导致线程池中的线程变为waiting状态

fyber / 2207人阅读

摘要:通过搜索引擎了解到以下观点提交到线程池的任务如果抛出异常会导致线程挂掉,遂将提交到线程池的任务中可能出现的异常进行了处理,确实解决了问题。

背景

项目中存在一些定时任务来更新数据库表,借助了线程池提供的一些能力,线上环境偶尔会出现网络波动导致服务实例无法连上数据库,只要出现了这种情况,就会导致数据不会再被更新,通过一些命令发现更新数据库的线程池中的所有线程都处于waiting状态。通过搜索引擎了解到以下观点:提交到线程池的任务如果抛出异常会导致线程挂掉,遂将提交到线程池的任务中可能出现的异常进行了处理,确实解决了问题。
同时也留下了一个疑问:为什么任务抛出的异常会导致线程处于waiting状态?

本篇文章的关注点主要集中在ScheduledThreadPoolExecutor.scheduleWithFixedDelay(..)这个方法上,对线程池的一些原理性的内容以及相关的术语不做过多描述。
执行流程

scheduleWithFixedDelay(..) 的大体运行过程(注,ScheduledThreadPoolExecutor类中还包含了execute,submit,schedule等方法,这些方法的逻辑基本是一致的):
1、首先对提交的任务(Runnable实例)进行一些包装,生成一个ScheduledFutureTask:

2、进入delayedExecute(..)方法,将生成的ScheduledFutureTask 放到线程池的任务队列(注:BlockingQueue)中;

3、进入ensurePrestart()方法,创建Worker实例开始处理线程:

4、最后就是addWorker方法了,此方法主要关注以下部分:

到这里,线程池中已经创建了线程,并且开始执行了。接下来就看看Worker线程是如何执行提交到线程池中的任务的。
5、上一步中,可以看到Worker中持有的线程已经开始运行了,而Worker中的线程是这么创建的:

所以,Worker中的线程start之后,则开始执行Worker中的run()方法(会进入到runWorker(..)方法)
6、上面的第1、2步中,会把构造的ScheduledFutureTask实例放到任务队列中,这里会再从任务队列中取出该实例(图中的while循环条件),然后再去调用该实例的run()方法:

getTask()方法:

7、ScheduledThreadPoolExecutor.ScheduledFutureTask#run()方法:

这里的outerTask就是第1步中的outerTask,其实就是要执行的任务本身。到了这里给出一个小结:对于周期性执行的任务,如果该任务执行失败,则后续其不会再被执行。
为了内容的完整性,下面给出上图中两个方法的流程:

到此,任务异常导致线程waiting的原因就明了了:
由于任务执行过程中抛出了异常,会造成ScheduledFutureTask不会再将自身放入到任务队列(BlockingQueue)中,即执行完之后,任务队列变成了一个空队列,而线程池中的Worker线程会以阻塞的方式从任务队列中去取任务(第6步),当队列为空时,会导致所有的线程都被阻塞而进入waiting状态。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77534.html

相关文章

  • 一个线程罢工的诡异事件

    摘要:结合之前的线程快照,我发现这个消费线程也是处于状态,和后面的业务线程池一模一样。本地模拟本地也是创建了一个单线程的线程池,分别执行了两个任务。发现当任务中抛出一个没有捕获的异常时,线程池中的线程就会处于状态,同时所有的堆栈都和生产相符。 showImg(https://segmentfault.com/img/remote/1460000018482477); 背景 事情(事故)是这样...

    BakerJ 评论0 收藏0
  • 线程池没你想的那么简单(续)

    摘要:前言前段时间写过一篇线程池没你想的那么简单,和大家一起撸了一个基本的线程池,具备线程池基本调度功能。线程池自动扩容缩容。回调以上就是线程池的构造函数以及接口的定义。所以我们在使用线程池时,其中的任务一定要做好异常处理。线程异常捕获的重要性。 showImg(https://segmentfault.com/img/remote/1460000019403163?w=1904&h=108...

    svtter 评论0 收藏0
  • java并发编程学习1--基础知识

    摘要:死亡状态线程退出有可能是正常执行完成也有可能遇见异常退出。类有新建与死亡状态返回其余状态返回判断线程是否存活。线程因某些原因进入阻塞状态。执行同步代码块的过程中执行了当前线程放弃开始睡眠进入就绪状态但是不会释放锁。 【java内存模型简介 JVM中存在一个主存区(Main Memory或Java Heap Memory),Java中所有变量都是存在主存中的,对于所有线程进行共享,而每个...

    huangjinnan 评论0 收藏0
  • Java并发总结

    摘要:限期阻塞调用方法等待时间结束或线程执行完毕。终止状态线程执行完毕或出现异常退了。和都会检查线程何时中断,并且在发现中断时提前放回。工厂方法将线程池的最大大小设置为,而将基本大小设置为,并将超时大小设置为分钟。 wait()、notify()、notifyAll() Object是所有类的基类,它有5个方法组成了等待、通知机制的核心:notify()、notifyAll()、wait()...

    szysky 评论0 收藏0
  • 线程小记

    摘要:死亡状态有两个原因会导致线程死亡方法正常退出而自然死亡。一个未捕获的异常终止了方法而使线程猝死。注意,放入的线程不必担心其结束,超过不活动,其会自动被终止。线程间相互干扰描述了当多个线程访问共享数据时可能出现的错误。 线程 进程与线程的区别 线程是指进程内的一个执行单元,也是进程内的可调度实体。一个程序至少有一个进程,一个进程至少有一个线程。 线程的五大状态 新建状态(New):例如...

    suxier 评论0 收藏0

发表评论

0条评论

fyber

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<