使用jstack排查多线程死锁、阻塞
问题:
针对线上多线程死锁、阻塞,跑着跑着就卡住了
查看线上线程池的状态
jstack用于生成java虚拟机当前时刻的线程快照。
线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
jstack:java堆栈跟踪工具
jstack用于生成java虚拟机当前时刻的线程快照。
线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
命令格式:
- jstack [ option ] pid
- jstack [ option ] executable core
- jstack [ option ] [server-id@]remote-hostname-or-IP
- 执行
jstack
命令,将得到进程的堆栈信息。我一般使用jstack -l pid
来得到长列表,显示其详细信息。 - 有时线程挂起的时候,需要执行
jstack -F pid
来获取。 - 在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,我们才确定问题的典型性。
- 堆栈信息只是一种参考,一些正常RUNNING的线程,由于复杂网络环境和IO的影响,也是有问题的,用jstack就无法定位,需要结合对业务代码的理解。
使用:查看服务进程的线程情况
- jstack -l
taskExecutor- 为线程词的名称
java.lang.Thread.State 线程的状态
具体为线程的执行到什么动作 代码行数
- 一个Thread对象可以有多个状态,在java.lang.Thread.State中,总共定义六种状态:
- 1、NEW
- 线程刚刚被创建,也就是已经new过了,但是还没有调用start()方法,jstack命令不会列出处于此状态的线程信息
- 2、RUNNABLE #java.lang.Thread.State: RUNNABLE
- RUNNABLE这个名字很具有欺骗性,很容易让人误以为处于这个状态的线程正在运行。事实上,这个状态只是表示,线程是可运行的。我们已经无数次提到过,一个单核CPU在同一时刻,只能运行一个线程。
- 3、BLOCKED # java.lang.Thread.State: BLOCKED (on object monitor)
- 线程处于阻塞状态,正在等待一个monitor lock。通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态。
- 4、WAITING
- 等待状态,调用以下方法可能会导致一个线程处于等待状态:
- Object.wait 不指定超时时间 # java.lang.Thread.State: WAITING (on object monitor)
- Thread.join with no timeout
- LockSupport.park #java.lang.Thread.State: WAITING (parking)
- 例如:对于wait()方法,一个线程处于等待状态,通常是在等待其他线程完成某个操作。本线程调用某个对象的wait()方法,其他线程处于完成之后,调用同一个对象的notify或者notifyAll()方法。Object.wait()方法只能够在同步代码块中调用。调用了wait()方法后,会释放锁。
- 5、TIMED_WAITING
- 线程等待指定的时间,对于以下方法的调用,可能会导致线程处于这个状态:
- Thread.sleep #java.lang.Thread.State: TIMED_WAITING (sleeping)
- Object.wait 指定超时时间 #java.lang.Thread.State: TIMED_WAITING (on object monitor)
- Thread.join with timeout
- LockSupport.parkNanos #java.lang.Thread.State: TIMED_WAITING (parking)
- LockSupport.parkUntil #java.lang.Thread.State: TIMED_WAITING (parking)
- 6、TERMINATED
- 线程终止。
在dump 文件里,写法可能不太一样:
- 死锁,Deadlock(重点关注)
- 执行中,Runnable
- 等待资源,Waiting on condition(重点关注)
- 等待获取监视器,Waiting on monitor entry(重点关注)
- 对象等待中,Object.wait() 或 TIMED_WAITING
- 暂停,Suspended
- 阻塞,Blocked(重点关注)
- 停止,Parked
Runnable
线程正在运行中。
一般指该线程正在执行状态中,该线程占用了资源,正在处理某个请求,有可能正在传递SQL到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。
Deadlock
- "t2" prio=6 tid=0x02bcf000 nid=0xc70 waiting for monitor entry [0x02f6f000]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at com.demo.DeadLock$2.run(DeadLock.java:40)
- - waiting to lock <0x22a297a8> (a java.lang.Object)
- - locked <0x22a297b0> (a java.lang.Object)
- Locked ownable synchronizers:
- - None
- "t1" prio=6 tid=0x02bce400 nid=0xba0 waiting for monitor entry [0x02f1f000]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at com.demo.DeadLock$1.run(DeadLock.java:25)
- - waiting to lock <0x22a297b0> (a java.lang.Object)
- - locked <0x22a297a8> (a java.lang.Object)
- Locked ownable synchronizers:
- - None
上面是一个典型的死锁堆栈,t1线程lock在地址
0x22a297a8
,同时t2线程在waiting to lock
这个地址。更有意思的是,堆栈也记录了发生死锁的代码行数,这对我们定位问题起到很大的帮助。
Wait on condition
等待资源,或等待某个条件的发生。具体原因需结合 stacktrace来分析。
常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。
关键字:
TIMED_WAITING
,sleeping
,parking
。TIMED_WAITING可能是调用了有超时参数的wait所引起的。parking指线程处于挂起中。
下面是一个典型的sleep引起的
Wait on condition
。
- "thread-1" prio=10 tid=0x00007fbe985cd000 nid=0x7bc6 waiting on condition [0x00007fbe65848000]
- java.lang.Thread.State: TIMED_WAITING (sleeping)
- at java.lang.Thread.sleep(Native Method)
- at com.xxx.MonitorManager$2.run(MonitorManager.java:124)
- at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
- at java.lang.Thread.run(Thread.java:662)
如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读 写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。可以结合其他网络分析工具定位问题。如下面的堆栈,线程等待在LinkedBlockingQueue上等待数据的生成。
- 如果堆栈信息明确是应用代码,则证明该线程正在等待资源。一般是大量读取某资源,且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取。
- "IoWaitThread" prio=6 tid=0x0000000007334800 nid=0x2b3c waiting on condition [0x000000000893f000]
- java.lang.Thread.State: WAITING (parking)
- at sun.misc.Unsafe.park(Native Method)
- - parking to wait for <0x00000007d5c45850> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
- at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
- at java.util.concurrent.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:440)
- at java.util.concurrent.LinkedBlockingDeque.take(LinkedBlockingDeque.java:629)
- at com.xxx.ThreadIoWaitState$IoWaitHandler2.run(ThreadIoWaitState.java:89)
- at java.lang.Thread.run(Thread.java:662)
Waiting on monitor entry和Object.wait()
意味着线程在等待进入一个临界区
Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。
- 所有期待获得锁的线程,在锁已经被其它线程拥有的时候,这些期待获得锁的线程就进入了
Object Lock
的entry set
区域。 - 所有曾经获得过锁,但是由于其它必要条件不满足而需要wait的时候,线程就进入了
Object Lock
的wait set
区域 。 - 在
wait set
区域的线程获得notify/notifyAll
通知的时候,随机的一个Thread(notify)或者是全部的Thread(notifyALL)从Object Lock的wait set区域进入了entry set中。 - 在当前拥有锁的线程释放掉锁的时候,处于该
Object Lock
的entryset
区域的线程都会抢占该锁,但是只能有任意的一个Thread能取得该锁,而其他线程依然在entry set
中等待下次来抢占到锁之后再执行。
看下面的堆栈,线程在等待数据库连接池返回一个可用的链接
- " DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
- - waiting to lock <0xe0375410> (a beans.ConnectionPool)
- at xxx.getTodayCount(ServiceCnt.java:111)
- at xxx.ServiceCnt.insertCount(ServiceCnt.java:43)
- "DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
- - waiting to lock <0xe0375410> (a beans.ConnectionPool)
- at xxx.ServiceCnt.getTodayCount(ServiceCnt.java:111)
- at xxx.ServiceCnt.insertCount(ServiceCnt.java:43)
- " DB-Processor-3" daemon prio=5 tid=0x00928248 nid=0x8b waiting for monitor entry [0x000000000825d080]
- java.lang.Thread.State: RUNNABLE
- at oracle.jdbc.driver.OracleConnection.isClosed(OracleConnection.java:570)
- - waiting to lock <0xe03ba2e0> (a oracle.jdbc.driver.OracleConnection)
- at beans.ConnectionPool.getConnection(ConnectionPool.java:112)
- - locked <0xe0386580> (a java.util.Vector)
- - locked <0xe0375410> (a beans.ConnectionPool)
- at xxx.Cue_1700c.GetNationList(Cue_1700c.java:66)
- at org.apache.jsp.cue_1700c_jsp._jspService(cue_1700c_jsp.java:120)
Blocked
线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
如果线程处于Blocked状态,但是原因不清楚。可以使用jstack -m pid
得到线程的mixed信息。
- ----------------- t@13 -----------------
- 0xff31e8b8 ___lwp_cond_wait + 0x4
- 0xfea8c810 void ObjectMonitor::EnterI(Thread*) + 0x2b8
- 0xfeac86b8 void ObjectMonitor::enter2(Thread*) + 0x250
例如,以上信息表明,线程在尝试进入同步块时阻塞了。
使用jstack排查多线程死锁、阻塞的更多相关文章
- jstack分析线程死锁
一.介绍 jstack是java虚拟机自带的一种堆栈跟踪工具.jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项&qu ...
- Java 多线程 死锁 隐性死锁 数据竞争 恶性数据竞争 错误解决深入分析 全方向举例
在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同.因此,在编写多线程程序时,事先认 ...
- Linux下面 多线程死锁问题的调试
最近写服务,经常是单进程,多线程的,加了各种锁,很担心出现死锁问题,专门学习了一下死锁问题的诊断. 死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种 ...
- 29、Java并发性和多线程-非阻塞算法
以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在 ...
- java线程基础巩固---多线程死锁分析,案例介绍
之前已经学习了关于同步锁的知识,但是在实际编写多线程程序时可能会存在死锁的情况,所以这次来模拟一下死锁,并且学会用一个命令来确认是否程序已经出现死锁了,下面开始: 首先新建两个类: 此时当然得到Oth ...
- Python | 多线程死锁问题的巧妙解决方法
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第25篇文章,我们一起来聊聊多线程开发当中死锁的问题. 死锁 死锁的原理非常简单,用一句话就可以描述完.就是当多线程 ...
- Java多线程死锁的产生实例
死锁产生的四个必要条件: (1) 互斥条件:一个资源每次只能被一个进程使用.(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放.(3) 不剥夺条件:进程已获得的资源,在末使用完 ...
- [翻译]:SQL死锁-阻塞探测
到了这篇,才是真正动手解决问题的时候,有了死锁之后就要分析死锁的原因,具体就是需要定位到具体的SQL语句上.那么如何发现产生死锁的问题本质呢?下面这篇讲的非常细了,还提到了不少实用的SQL,但对我个人 ...
- [翻译]:SQL死锁-阻塞
一般情况下死锁不是一步到位的,它必须满足特定的条件,然后形成资源的循环依赖才会产生死锁,死锁之前一定会出现阻塞,由阻塞升级才有可能出现死锁,所以我们有必要了解系统中都有哪些已经被阻塞的锁. 我在解决共 ...
随机推荐
- 英语阅读——Love and logic:The story of a fallacy
这篇文章是<新视野大学英语>第四册的第一单元的文章,读着挺有趣,便拿过来分享一下. 1 I had my first date with Polly after I made the tr ...
- 二级目录下的运行main.py,找不到上级目录的解决方法
import os, sys sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
- VMWare虚拟机应用介绍
目录 一:VMWare简介 二:VMWare启动第一个虚拟机 一:VMWare简介 VMWare虚拟机软件是一个"虚拟PC"软件,它使你可以在一台机器上同时运行二个或更多Win ...
- 易优CMS:foreach的基础用法
[基础用法] 名称:foreach 功能:数据/记录循环输出标签(注:类似与volist标签,只是更加简单,没有太多额外的属性.) 语法: {eyou:channel type='top'} {e ...
- Javase之集合泛型
集合泛型知识 泛型 是一种把类型明确工作推迟到创建对象或者调用方法的时候才明确的特殊类型. 也称参数化类型,把类型当成参数传递. 在jdk1.5中出现.一般来说经常在集合中使用. 格式 <数据类 ...
- awk 输出前 N 列的最简单方法
最近遇到一种场景,需要输出一个文本信息的前 N 列. 众所周知 cut 可以指定分隔符并指定列的范围,如 cut -d' ' -f-4 就是以空格为分隔符输出前 4 列.但是 cut 的分隔符只能是一 ...
- hadoop 自定义OutputFormat
1.继承FileOutputFormat,复写getRecordWriter方法 /** * @Description:自定义outputFormat,输出数据到不同的文件 */ public cla ...
- C语言中的volatile关键字简介
C语言中的volatile关键字简介: (1)含义: volatile关键字的意思是可能会被外来的意想不到的改变.它的作用是:优化器在使用该关键字定义的变量时,直接从内存中读取原始的数 ...
- shell脚本一次性将tab制表符改为4空格的方法
问题描述: 今天需要修改一些bash脚本,因为考虑到pycharm里面能够直接写,而我用pycharm比较多,所以直接用pycharm写了,由于改的那个bash脚本是别的同事写的,里面的缩进都是用的T ...
- redis 进程使用root用户启动 -- 整改方案
最近内部风险整改, 各种进程使用root身份进行启动不符合要求, 于是各路神仙各施其法,为的就是让 某进程不以root 启动: 先以 redis 为例: 原有进程如下: #超一流标准的执行文件位置及配 ...