摘要:内外层异步使用同一线程池执行任务导致线程数不够,产生线程死锁
问题:
-
某台机器查询接口半夜12点有问题,当时同事将问题机器拉出,并进行dump
-
由错误日志可以得出是线程的任务队列满导致任务丢弃报错
-
从错误日志不能看出具有哪个线程有问题,通过线程统计线程数量看出是query_callback线程池,然后看监控发现队列确实满了。
行动:
- 先观察拉取后的线程栈,发现线程全部是WAITING状态,线程被阻塞在业务代码的异步的get方法上。
- 发现代码使用了嵌套的completableFuture.thenApplyAsync,在外层thenApplyAsync的代码内部使用了return返回了另一completableFuture的thenApplyAsync,并且使用了同一executor
-
通过分析得出所有线程阻塞原因:
-
外层异步和内层异步使用同一线程池
-
在某个时间范围内,线程池的所有活跃线程都去执行外部异步调用,导致可用线程数为0
-
每个外层异步都会从线程池中申请一个线程执行内层异步,由于此时已经没有线程,内层异步的任务被排入任务队列中,没有执行
-
外层异步会在内层异步的get方法处等待,而内层异步等待外层异步完成释放线程,因此进入死锁
-
-
解决:
-
使用线程隔离:内外层使用不同线程池
-
-
效果:线程死锁解决,线程池任务队列满导致抛出的异常不再产生