记一次多线程问题的解决过程

公司内部某系统是基于多线程的环境,最近不知什么原因,开始不定时down机,查看系统进程,没有问题,且系统中定时输出的日志也没有间断,只是不进行任务的处理。之前任务多、时间紧,重启一下能暂时解决问题,只是后来越发频繁,今天集中精力排查原因。


  • 步骤1:
    利用top命令查看系统占用,如下。cpu和内存占用都还可以,应该不是出现了内存泄露或者死循环之类的问题,况且之前系统运行一直良好,只是最近才出现问题。开始从其他方向入手。

当然了,这里我其实多绕了一步,还是进行了一次heap dump,然后利用MAT工具对堆栈信息快速过了一下,基本用法可以参照这篇博客http://blog.csdn.net/rachel_luo/article/details/8992461。可以看到,这里显示BlockingQueue占用空间最多,虽然最后发现问题不是这个队列导致的,但在这里,为最后的快速定位问题提供了思路。

  • 步骤2:
    既然是多线程环境,而且系统又没有挂掉,应该是某些工作线程被block住了。照着这个思路,又把线程信息给dump了出来。这里可以利用jdk自带的jstack命令,我逃了个懒,直接利用jvisualvm可视化工具做掉了。像下面这样:

可以直接在右侧窗口看到dump的thread信息。经过排查,工作线程都是在WAITTING状态,而且是阻塞在了同一个地方。这就好办了,根据提示,在HarvestHolder类的第30行进行查看(如下),发现是往一个BlockingQueue中普通数据时阻塞住了。所以判定,肯定是队列满了。那又是为什么会一直put不进去呢,这就简单了,肯定是没有消耗或者消费者进程因为某些原因不执行了。

接下来,只要找到消费者进程(或线程),是在哪里、以什么方式消耗的,然后再分析原因。于是找到了这段代码:

很基础的一个线程启动方式,BlockingQueue的get()方法也是阻塞性质的,中规中矩,好像并没有什么问题。但其实仔细思考过会发现,如果process()方法抛出异常了怎么办。究竟会不会抛出异常,要进入到方法体内查看具体代码,实际情况是,里面只有一段非常简单了入库逻辑,但刚好最近因为某些原因,数据库连接不稳定,导致经常超时,所以这里会抛出异常,而且也没有捕获处理,然后导致这个消费者线程挂掉,影响了整个系统的继续运转。
根据不同的业务场景,相应的情况会有不同的处理方法。这里我加了try...catch(){},并进行了一定的重试。


系统比较古老,而且也基本很少接触,所以刚开始排查问题,是有点不顺的。不过还好,java平台可以借助的工具比较多,善于利用,排查问题还是不难的。基本上常用的命令工具有:

  1. jps:查看JVM中运行的进程状态信息
  2. jstack:查看线程堆栈信息
  3. jmap:堆内存信息
  4. jstat:GC相关,用来性能调优
posted @ 2017-06-18 14:50  是L先生啊  阅读(805)  评论(4)    收藏  举报