我开发的worker,每隔几个月线上都会阻塞一次,一直都没查出问题。今天终于了了这个心结。把解决过程总结下和大家分享。

首先用jstack命令打出这个进程的全部线程堆栈。拿到线程dump文件之后,搜索自己的worker名字。

  1. "DefaultQuartzScheduler_Worker-10" prio=10 tid=0x00007f55cd54d800 nid=0x3e2e waiting for monitor entry [0x00007f51ab8f7000]
  2. java.lang.Thread.State: BLOCKED (on object monitor)
  3. at com.jd.chat.worker.service.impl.NewPopAccountSyncServiceImpl.addAccounts(NewPopAccountSyncServiceImpl.java:86)
  4. - waiting to lock <0x0000000782359268> (a com.jd.chat.worker.service.impl.NewPopAccountSyncServiceImpl)
  5. at com.jd.chat.worker.service.timer.AccountIncSyncTimer.run(AccountIncSyncTimer.java:114)
  6. at com.jd.chat.worker.service.timer.AbstractTimer.start(AbstractTimer.java:44)
  7. at com.jd.chat.worker.service.timer.AbstractTimer.doJob(AbstractTimer.java:49)
  8. at com.jd.chat.worker.web.context.StartAppListener$TimerJob.execute(StartAppListener.java:188)
  9. at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
  10. at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
  11. - locked <0x0000000783641c68> (a java.lang.Object)

很快便找到了线程在哪一行被阻塞。但是就凭这么点信息,并不能查出问题的真正原因,这里推荐一个工具,叫tda.bat。同事给我的,网上应该有下载。把这个dump文件导入到tda中。找到阻塞的线程。阻塞的线程是红色的。

之所以说这个软件好,是因为当你找到blocked的线程后,界面的下方,会打出阻塞的更详细的线程堆栈。截取这个堆栈的部分信息。

  1. at org.mariadb.jdbc.MySQLPreparedStatement.execute(MySQLPreparedStatement.java:141)
  2. at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172)
  3. at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172)
  4. at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(SqlExecutor.java:80)
  5. at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteUpdate(MappedStatement.java:216)
  6. at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:94)
  7. at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.update(SqlMapExecutorDelegate.java:457)
  8. at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.update(SqlMapSessionImpl.java:90)
  9. at org.springframework.orm.ibatis.SqlMapClientTemplate$9.doInSqlMapClient(SqlMapClientTemplate.java:380)
  10. at org.springframework.orm.ibatis.SqlMapClientTemplate$9.doInSqlMapClient(SqlMapClientTemplate.java:1)
  11. at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:200)
  12. at org.springframework.orm.ibatis.SqlMapClientTemplate.update(SqlMapClientTemplate.java:378)
  13. at com.jd.im.data.dataresource.ImSqlMapClientTemplate.retriedWithoutAnyInterventionUpdate(ImSqlMapClientTemplate.java:169)
  14. at com.jd.im.data.dataresource.ImSqlMapClientTemplate.update(ImSqlMapClientTemplate.java:137)
  15. at com.jd.chat.dao.impl.WriteDaoImpl.update(WriteDaoImpl.java:21)
  16. at com.jd.chat.zone.service.impl.GroupServiceImpl.updateRoute(GroupServiceImpl.java:766)
  17. at com.jd.chat.worker.service.impl.NewPopAccountSyncServiceImpl.addAccounts(NewPopAccountSyncServiceImpl.java:267)
  18. - locked <0x0000000782359268> (a com.jd.chat.worker.service.impl.NewPopAccountSyncServiceImpl)

这个才是真正有用的堆栈!它告诉了我程序是在执行SQL的时候,SQL发生死锁,于是线程被阻塞。它还提供了更有用的信息,那就是到底是哪个SQL导致的死锁。堆栈的倒数第三行指示了导致死锁的SQL。

但是一定要用这个工具才能找到具体的原因吗?答案当然是NO!

告诉大家怎么不通过工具找到阻塞的真正原因!

刚刚通过“BLOCKED”关键字搜到了线程堆栈,找到它的线程名“DefaultQuartzScheduler_Worker-10”。OK,然后,把最后的10改成1,也就是“DefaultQuartzScheduler_Worker-1”,然后再拿这个关键字搜索整个进程堆栈。

  1. "DefaultQuartzScheduler_Worker-1" prio=10 tid=0x00007f55cd2aa000 nid=0x3e25 runnable [0x00007f51b02c0000]
  2. java.lang.Thread.State: RUNNABLE
  3. at java.net.SocketInputStream.socketRead0(Native Method)
  4. at java.net.SocketInputStream.read(SocketInputStream.java:129)
  5. at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
  6. at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
  7. at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
  8. - locked <0x0000000791370d50> (a java.io.BufferedInputStream)
  9. at org.mariadb.jdbc.internal.common.packet.buffer.ReadUtil.readFully(ReadUtil.java:82)
  10. at org.mariadb.jdbc.internal.common.packet.buffer.ReadUtil.readFully(ReadUtil.java:92)
  11. at org.mariadb.jdbc.internal.common.packet.RawPacket.nextPacket(RawPacket.java:77)
  12. at org.mariadb.jdbc.internal.common.packet.SyncPacketFetcher.getRawPacket(SyncPacketFetcher.java:67)
  13. at org.mariadb.jdbc.internal.mysql.MySQLProtocol.getResult(MySQLProtocol.java:891)
  14. at org.mariadb.jdbc.internal.mysql.MySQLProtocol.executeQuery(MySQLProtocol.java:982)
  15. at org.mariadb.jdbc.MySQLStatement.execute(MySQLStatement.java:280)
  16. - locked <0x0000000791370678> (a org.mariadb.jdbc.internal.mysql.MySQLProtocol)
  17. at org.mariadb.jdbc.MySQLPreparedStatement.execute(MySQLPreparedStatement.java:141)

贴出这个进程堆栈的一部分。这个进程堆栈其实也就是刚刚tda软件界面下方展示的导致线程阻塞的真正的堆栈!这个线程是runnable状态的,可惜mysql是锁死的。也就是说阻塞在了mysql里。

感觉这是一个由张三的命案牵出李四的命案的故事。

java线程阻塞问题排查方法的更多相关文章

  1. Java线程阻塞方法sleep()和wait()精炼详解

    版权声明:因为个人水平有限,文章中可能会出现错误,如果你觉得有描述不当.代码错误等内容或者有更好的实现方式,欢迎在评论区告诉我,即刻回复!最后,欢迎关注博主!谢谢 https://blog.csdn. ...

  2. java线程阻塞(sleep,suspend,resume,yield,wait,notify)

    为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了 ...

  3. Java线程 - sleep()和wait()方法的区别, 线程阻塞BLOCKED和等待WAITING的区别

    一. sleep()和wait()方法的区别 sleep()方法 sleep()方法是Thread类的方法,通过其定义可知是个native方法,在指定的时间内阻塞线程的执行.而且从其注释中可知,并不会 ...

  4. JAVA 线上问题排查方法

    CPU 磁盘 内存 GC问题 网络 线上故障主要会包括cpu.磁盘.内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍. 同时例如jstack.jma ...

  5. 停止Java线程,小心interrupt()方法

    来源:http://blog.csdn.net/wxwzy738/article/details/8516253 程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决 ...

  6. JAVA线程sleep和wait方法区别

    一. sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复,调用sleep 不会释放对象锁.由于没有释放对象锁,所以不能 ...

  7. 多线程之Java线程阻塞与唤醒

    线程的阻塞和唤醒在多线程并发过程中是一个关键点,当线程数量达到很大的数量级时,并发可能带来很多隐蔽的问题.如何正确暂停一个线程,暂停后又如何在一个要求的时间点恢复,这些都需要仔细考虑的细节.在Java ...

  8. java线程阻塞唤醒的四种方式

    java在多线程情况下,经常会使用到线程的阻塞与唤醒,这里就为大家简单介绍一下以下几种阻塞/唤醒方式与区别,不做详细的介绍与代码分析 suspend与resume Java废弃 suspend() 去 ...

  9. Java 线程安全的实现方法

    概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体, 程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和 ...

随机推荐

  1. 数学图形(1.29) cochleoid曲线

    它也算是一种螺线吧 相关软件参见:数学图形可视化工具,使用自己定义语法的脚本代码生成数学图形.该软件免费开源.QQ交流群: 367752815 #http://www.mathcurve.com/co ...

  2. XSS盗COOKIE

    XSS攻击:跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆.故将跨站脚本攻击缩写为XSS. XSS是一种 ...

  3. [置顶] OpenJDK源码研究笔记(九)-可恨却又可亲的的异常(NullPointerException)

    可恨的异常 程序开发过程中,最讨厌异常了. 异常代表着程序出了问题,一旦出现,控制台会出现一屏又一屏的堆栈错误信息. 看着就让人心烦. 对于一个新人来讲,遇到异常经常会压力大,手忙脚乱,心生畏惧. 可 ...

  4. Zclip:复制页面内容到剪贴板兼容各浏览器

    WEB开发中,要让用户复制页面中的一段代码.URL地址等信息,为了避免用户拖动鼠标再进行右键复制操作而可能出现的差错,我们可以直接在页面中放置一个复制按钮,只需要轻轻一点这个复制按钮,内容将会被复制, ...

  5. Android -- Vibrator

    Vibrator                                                                                    public c ...

  6. Node Server零基础——开发环境文件自动重载

    收录待用,修改转载已取得腾讯云授权 前言 在 web 前端开发中,我们会借助 Grunt.Gulp 和 Webpack 等工具的 Watch 模块去监听文件变化,那服务端应该怎么做?其实文件变化的监听 ...

  7. java中 this和super的差别

    this表示当前调用方法的对象的引用: (谁调用这种方法,谁就是这个对象,这个this就是它的引用) 比方: <pre name="code" class="jav ...

  8. 【Python】【Nodejs】下载单张图片到本地,Python和Nodejs的比较

    Python版本: # 下载单张图片到本地,看用时多少 import urllib.request import datetime starttime = datetime.datetime.now( ...

  9. Vim的使用 区域选择

    1.Visual Block 区域选择,这里的Visual表示视觉,图像,可视化. 2.    小写v:字符选择     shift+v(大写V):行选择               ctrl+v:矩 ...

  10. PHP快速入门 如何下载和安装

    1 从该网站下载 http://www.appservnetwork.com/ 2 双击安装,注意安装路径和安装组件 按下面的填写,端口不要和IIS冲突,字符编号为UTF-8 安装完成之后,注意安装文 ...