开发者学堂课程【线上问题排查利器 Alibaba Arthas(上):Jvm 相关命令之:thread】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/746/detail/13191
JVM 的相关命令之:thread
内容介绍:
一、作用
二、参数说明
三、实际操作举例
一、作用
Thread 是线程相关的命令,刚刚在上面也看到了thread的内容,在dashboard里面也能看到 thread,但是并不能详细的显示每一个线程的具体信息,所以要通过thread这条命令来查看当前 JVM 的线程堆栈信息。
二、参数说明
下图有一些参数,例如可以在后面直接添加一个数字,来表示此线程的 ID,即具体查看某一个线程的信息。第二个【n:】代表可以指定显示最忙的前N个线程的信息,对于后期调试,或者查找问题方面提供了很大的便捷之处,可以看到哪几个线程是最繁忙的状态。第三个【b】可以显示当前处于阻塞状态的线程。第四个【i】可以指定采样的时间间隔,默认单位是毫秒,可以指定它时间长一点或者短一点。为毫秒参数。
参数名称参数说明
数字线程 id
【n:】指定最忙的前N个线程并打印堆栈
【b】找出当前阻塞其他线程的线程
【i】指定cpu占比统计的采样的间隔,单位是毫秒
三、实际操作举例
1.第一步
(1)展示当前最忙的前3个线程并打印堆栈
(2)令thread=3
现在可以开始操作,此处的数学游戏还在运行,下方输入thread-n3后点击回车。可以看到此时最忙的三个线程,在上面显示是最繁忙的三个线程,但这个是tomcat的当中的一些线程,因为刚才启动的就是tomcat的线程,这里可以显示可以看到总共有三个线程,以及三个ID:“ID=59”、“ID=2”、“ID=3”,这是指其最繁忙的三个线程,其中有一个cpu占比甚至达到100%。所以这个可以显示最繁忙的三条线程,也就是刚才提到【n:】参数,
“as-command-executedaemon”Id=59cpuUsage=100%RUNNABLE
“ReferenceHandler”Id=2cpuUsage=0%WAITINGonjava.lang.
ref.References$Lock@b1cdf39
“Finalizer”Id=3cpuUsage=0%WAITINGonjava.lang.ref.
ReferenceQueue$Lock@3b5c3d40
2.第二步
(1)当没有参数时,显示所有线程的信息
(2)Thread
接下来,如果不带参数的情况下,可以看到显示的是所有线程,tomcat当中启动所有的线程,这是指不带参数的情况。此参数上面显示的与刚刚看到的disco面板里看到的是一样的,只是这里可以详细的看其中的任意一个。
3.第三步
(1)显示一号线程的运行堆栈
(2)Thread1
例如这里需要查看一号线程,可以发现“main”就是一号线程。需要查看一号线程,直接在下面 thread 的后面添加线程的编号就可以完成。输入thread1,就可以看到一号线程的整个运行状态。一号线程启动用到的是 main 函数,main 函数启动,然后就一直往上调用它的线程堆栈。
4.第四步
(1)找出当前阻塞其他线程的线程,有时候发现应用卡住了,通常是由于某个线程拿住了某个锁,并且其他线程都在等待这把锁造成的。为了排查这类问题,arthas 提供了 thread-b,一键找出罪魁祸首。
(2)thread-b
$thread-b
"http-bio-8080-exec-4"Id=27TIMEDWAITING
atiavalangThread.sleep(NativeMethod)
attest.arthas.TestThreadBlocking.doGet(Test
ThreadBlocking.java:22)
-lockediava.lang.Obiect@725be470<----butblocks4other
threads!
atjavax.servlet.http.HttpServlet.service(HttpServletjava:62
4)
atjavax.servlet.http.Http.Servlet.service(HttpServlet.java:
731)
然后再来看处于阻塞状态的一个线程-b 的参数,此处也要是阻塞的状态,正好此处准备了素材,也就是此处 my-demo.jar 的文件,在资料里面也有提供,这是中间正好有一个死锁的案例。
/**
*请编写程序,演示一下死锁的情况
publicclassDemo21DeadLock{
publicstaticvoidmain(String[]args){
DinnerThreaddinnerThread=newDinnerThread();
newThread(dinnerThread,name:"Jack").start();
newThread(dinnerThread,name:"Rose").start();
}
}
classDinnerThreadimplementsRunnable{
Stringlock1=“第1根筷子";
Stringlock2=“第2根筷子";
@Override
例如现在启动了两个线程,一个叫 Jack,一个叫 Rose。这两个线程,因为互相使用了锁的嵌套,所以会导致处于死锁的状态,正好就可以看到是哪个线程在死锁。这里已经把其打包成 java 包,这时就可以进行运行。可以发现有 my-demo,直接输入 Java-cpmy-demo.jar,因为 my-demo 不是直接可以执行的完整项目,其只是打包了很多class 文件,如果需要运行其中的一个 class,就在后面输入其包名加类名,例如这个案例就输入包名 com.itheima.day13.essential,在输入类名 Demo21DeadLock。点击回车(不一定死锁,死锁情况由概率决定,因为是由 Jack 和 Rose 两个线程各自执行,再决定能否进入死锁状态),操作几次后进入死锁状态。在my-demo中操作死锁后,进入 mydate.log中检查是否真正死锁,以及哪个线程死锁了。这里再次启动 arthas-boot.jar,点击回车,发现是Demo21DeadLock线程,也就是9186【2】,输入2后点击回车,发现因为端口冲突导致系统报错,提示3658已经被其他的进程占用,这时系统提示可以输入 java-jararthas-boot.jar--telnet-port9998--http-port-1,也就是换端口号,这里可以复制粘贴,只需要换端口号就可以,这时候又需要选择,这里选择死锁的一号,点击回车,然后其就会连附死锁的进程。当然再输入 thread,这时可以看到的所有线程,是属于刚刚另外启动的死锁。可以看到 Jack 和 Rose 两个线程,已经
显示 blocked,处于死锁阻塞的状态了。当然这时也可以再输入 three-b,看是谁锁住谁了。点击回车,可以
看到 ID=9(Jack)是锁。而锁的拥有者是id=10(Rose)拥有,所以导致了死锁,这个地方显示用 string 作为锁。这样就阻塞了其他的线程,也有利于大家进行整段排错。
5.第五步
(1)指定采样时间间隔,每过1000ms 采样,显示作占时间的三个
线程
(2)Thread-i1000-n3
这里先退出 my-demo.jay,回到 mydate.log,再输入 thread-i
1000-n3,也就是每过1000ms 采样,显示最繁忙的三个线程。
“as-command-execute-daemon”Id=60cpuUsage-22%RUNNABLE
“ajp-nio-8009-AsyncTimeout”Id=45cpuUsage=12%
TIMED_WAITING
“http-nio-8080-AsyncTimeout”Id-31cpuUsage=10%TIMED
_WAITING
这里显示了三个进程的cpu占比分别达到了22%、12%、10%,而且是过1s(1000ms)取一次样,这就是有关线程的命令。
6.第六步
(1)查看处于等待状态的线程
(2)Thread--stateWAITING
刚刚在thread 里看到有很多种状态:运行的,等待的。需要查看某一种状态的线程,就可以输入 Thread--stateWAITING 查看 waiting状态线程,这时可以看到所有黄色的是属于等待状态的线程。这也是thread 的比较重要的一条命令。








