Java各种线程死锁问题

病症

web服务跑着跑着响应越来越慢,最终网页白页的,一查进程还活着,socket连接高,十有八九线程死锁没得到释放,最后没线程资源执行任务导致白页等待。

最有效的排查方法

最直接的排查方法就是,dump下内存快照,去查看线程信息,看看是否有大量的线程处于等待状态。基本上就能定位是哪里的问题。

常见的几种线程死锁的场景

调用线程池执行任务,任务内又调用线程池执行任务

原因:

使用了同一个线程池,开启线程异步执行任务 A,任务 A 中又开启了线程异步执行任务 B,在并发场景高的情况下,可能会出现线程池中得到线程执行的都是任务 A,所有的任务 A 都执行到开启线程执行任务 B的时间点,但是由于活跃线程都被任务 A 占用了,所以任务 B 都在线程池队列中等待,而任务 A 又都在等待任务 B 返回结果,最终导致线程得不到释放而耗尽

解决方案:

  1. 在同一个线程池中开启的线程在执行过程中不允许再开启该线程池中的其他线程去执行任务
  2. 多线程执行等待结果的时候,设置超时时间,不要无限等待

微服务间的循环调用

原因:

服务 A 调用服务 B,服务 B 又调用了服务 A。并发高时,循环调用导致了服务 A 的 web 容器线程数一下子上来,从而导致了服务 B 想要调用服务 A 得排队,继而导致服务 A 线程得不到释放,外部请求都在队列中排队,最终卡死。

解决方案:

  1. 微服务间避免循环调用,划清微服务的功能界限,只能单向调用
  2. 微服务请求一定要设置超时时间和熔断机制,避免雪崩

错误的使用方式

CountDownLatch#countDown方法没有放在finally中调用,CountDownLatch#await方法不设置超时时间等,在正常情况下都不会出现问题,但是一旦任务内出现未检查异常,就会导致countDown未执行,主线程可能就会一直等待

解决方案:

  1. 所用用到闭锁的地方,都需要将释放锁的逻辑放到 finally 中,且 await 都需要设置超时时间
  2. 代码提交的时候用 sonarlint 扫描,避免这些 block 问题
  3. 配置 actuator 监控线程池的使用状况,配置预警和告警通知,及时发现线程池状态不健康等问题
请登录后发表评论

    没有回复内容