JVM线程分析详解

java线程状态:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为"运行"。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕。

Java Monitor原理:

Monitor 是Java中用以实现线程之间的互斥与协作的主要手段,可以看成是对象或者Class的锁。每一个对象有且仅有一个monitor。Java 的 Object 类本身就是监视者对象,Java 对于 Monitor Object 模式做了内建的支持。

Object 类本身就是监视者对象!

每个 Object 都带了一把看不见的锁,通常叫 内部锁/Monitor 锁/Instrinsic Lock, 这把锁就是 监控锁。

synchronized 关键字修饰方法和代码块就是 同步方法

wait()/notify()/notifyAll() 方法构成监控条件(Monitor Condition)

线程和Monitor之间关系:

一个Monitor在某个时刻,只能被一个线程拥有,该线程就是Active Thread,

而其它线程都是Waiting Thread,分别在两个队列Entry Set和Wait Set里面等候。

在Entry Set中等待的线程状态是Waiting for monitor entry,

在Wait Set中等待的线程状态是in Object.wait()。

线程进入同步方法中:

为了继续执行临界区代码,线程必须获取 Monitor 锁。如果获取锁成功,将成为该监视者对象的拥有者。任一时刻内,监视者对象只属于一个活动线程(The Owner)

拥有监视者对象的线程可以调用 wait() 进入等待集合(Wait Set),同时释放监视锁,进入等待状态。

其他线程调用 notify() / notifyAll() 接口唤醒等待集合中的线程,这些等待的线程需要重新获取监视锁后才能执行 wait() 之后的代码。

同步方法执行完毕了,线程退出临界区,并释放监视锁。

进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。

拥有者(The Owner):表示某一线程成功竞争到对象锁。

等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

线程调用修饰:

表示线程在方法调用时,额外的重要的操作,一般出现在线程内容中,用来协助分析线程的状态。

locked <对象地址> 对象名: 表示当前线程通过synchronized关键字成功获取到了目标对象的监视器,拥有了在临界区操作的权限,状态一般为runnable。注意对象锁是可重入的,所以这里有2次锁住了同一个对象。

waiting to lock <对象地址> 对象名:

表示当前线程未能通过synchronized关键字获取到目标对象的监视器,状态一般为blocked。

waiting on<对象地址> 对象名:

表示当前线程通过synchronized关键字成功获取到了目标对象的监视器,但调用了wait方法,进入对象的等待区等待。在调用栈顶出现,线程状态为WAITING或TIMED_WATING。

parking to wait for <对象地址> 对象名:

park是基本的线程阻塞原语,不通过监视器在对象上阻塞。随concurrent包会出现的新的机制,与synchronized体系不同,具体介绍可以看stackoverflow上的回答传送门。

系统线程状态:

出现在线程信息第一行的末尾。

deadlock:死锁线程

runnable:表示线程运行中或I/O等待,线程状态一般为RUNNABLE。

in Object.wait():进入临界区之后,又调用了java.lang.Object.wait()方法等待,jvm线程状态一般为WAITING或TIMED_WAITING。

waiting for monitor entry:在等待进入一个临界区,线程状态一般状态为BLOCKED。

waiting on condition:线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析。

(1)如果堆栈信息明确是应用代码,则证明该线程正在等待资源,一般是大量读取某种资源且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取,或者正在等待其他线程的执行等。

(2)如果发现有大量的线程都正处于这种状态,并且堆栈信息中得知正等待网络读写,这是可能因为网络阻塞导致线程无法执行。

(3)还有一种常见的情况是该线程在sleep,等待sleep的时间到了,将被唤醒。

JVM线程状态:

java.lang.Thread.State: RUNNABLE 线程运行中或I/O等待 方法调用-无

java.lang.Thread.State: BLOCKED (on object monitor) 等待进入一个临界区 方法调用-synchronized

java.lang.Thread.State: TIMED_WAITING (parking) 线程等待唤醒,并且设置等待时长 方法调用-LockSupport.parkNanos(等待时长)、LockSupport.parkUntil(等待时长)

java.lang.Thread.State: TIMED_WAITING (sleeping) 线程等待唤醒,并且设置等待时长 方法调用-Thread.sleep(等待时长),Thread.join(等待时长)

java.lang.Thread.State: TIMED_WAITING (on object monitor) 线程在等待唤醒,并且设置等待时长 方法调用-Object.wait(等待时长)

java.lang.Thread.State: WAITING (parking) 线程等待唤醒,没有设置等待时长 方法调用-LockSupport.park()

java.lang.Thread.State: WAITING (on object monitor) 线程等待唤醒,并且没有设置等待时长 方法调用-Object.wait()

java.lang.Thread.State: WAITING (on object monitor) 线程等待唤醒,没有设置等待时长 方法调用-Thread.join()

jvm日志分析:

"ForkJoinPool.commonPool-worker-52"{线程名} #5688{线程ID} daemon prio=5{线程的优先级,取值范围为[1-10],默认为 5,数值越低越有优先} os_prio=0{线程在操作系统中的优先级} tid=0x00007f31eb3f3800 nid=0xa8ec{Native thread ID,本地操作系统相关的线程id,对应JVM 虚拟机中线程映射在操作系统中的线程编号。(这个nid就是我们用top -Hp pid 查看到的线程号,不过要用printf "%x\n"转换一下进制)} waiting on condition [0x00007f31944d0000]{系统线程状态}

java.lang.Thread.State: WAITING (parking){JVM线程状态}

at sun.misc.Unsafe.park(Native Method)

  • parking to wait for <0x00000004706e6598> (a java.util.concurrent.ForkJoinPool)

at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)

at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)

at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

Locked ownable synchronizers:

  • None

常见问题:

1)锁争用

如果出现大量的锁争用,可能是线程阻塞了!

#48线程获得了 <0x000000046b9b6078>对象的锁,进入了临界区;#49无法获取到锁,停留在Entry Set中,输出waiting to lock <0x000000046b9b6078>。

"hiveserver2-web-49-acceptor-2@23302924-ServerConnector@6d6d480c{HTTP/1.1,[http/1.1]}{0.0.0.0:10002}" #49 daemon prio=3 os_prio=0 tid=0x00007f31eaf33000 nid=0x6306 waiting for monitor entry [0x00007f31a3dfc000]

java.lang.Thread.State: BLOCKED (on object monitor) --JVM线程状态

at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:234)

  • waiting to lock <0x000000046b9b6078> (a java.lang.Object)

at org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:397)

at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:601)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)

at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)

at java.lang.Thread.run(Thread.java:748)

Locked ownable synchronizers:

  • None

"hiveserver2-web-48-acceptor-1@580fe4f0-ServerConnector@6d6d480c{HTTP/1.1,[http/1.1]}{0.0.0.0:10002}" #48 daemon prio=3 os_prio=0 tid=0x00007f31eaf31800 nid=0x6305 runnable [0x00007f31a3efd000]

java.lang.Thread.State: RUNNABLE

at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)

at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)

at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)

  • locked <0x000000046b9b6078> (a java.lang.Object)

at org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:397)

at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:601)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)

at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)

at java.lang.Thread.run(Thread.java:748)

Locked ownable synchronizers:

  • None

2)死锁

如果两个线程相互都被对方的线程锁锁住,这样就造成了死锁。

Thread1获取了<0x00000000d8251168>锁,等待<0x00000000d8251198>锁;Thread2获取了<0x00000000d8251198>锁,等待<0x00000000d8251168>锁。导致死锁!

"Thread2" daemon prio=5 tid=0x00007f1d3825f800 nid=0x2142 waiting for monitor entry [0x00007f1d16eeb000]

java.lang.Thread.State: BLOCKED (on object monitor)

at com.jvm.study.threaddump.deadlock.DeadLockMock$2.run(DeadLockMock.java:31)

  • waiting to lock <0x00000000d8251168> (a java.lang.String)

  • locked <0x00000000d8251198> (a java.lang.String)

"Thread1" daemon prio=5 tid=0x00007f1d3825e000 nid=0x2141 waiting for monitor entry [0x00007f1d16fec000]

ava.lang.Thread.State: BLOCKED (on object monitor)

at com.jvm.study.threaddump.deadlock.DeadLockMock$1.run(DeadLockMock.java:16)

  • waiting to lock <0x00000000d8251198> (a java.lang.String)

  • locked <0x00000000d8251168> (a java.lang.String)

3)等待区等待

JVM线程的状态是java.lang.Thread.State: TIMED_WAITING (on object monitor),线程调用了java.lang.Object.wait(Native Method)方法而进入了等待状态。

"Wait Set"中等待的线程状态就是 in Object.wait();

当线程获得了Monitor进入临界区之后,如果发现线程继续运行的条件没有满足,它就调用对象(通常是被synchronized的对象)的wait()方法,放弃了Monitor,进入"Wait Set"队列中。

只有当别的线程在该对象上调用了notify()或notifyAll()方法, "Wait Set" 队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,从而恢复到运行态。

"IPC Client (2108708444) connection to IT-CDH-Node02/10.11.16.36:8020 from hive" #5681 daemon prio=5 os_prio=0 tid=0x00007f31ccf4a000 nid=0xa8e4 in Object.wait() [0x00007f319aa33000]

java.lang.Thread.State: TIMED_WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at org.apache.hadoop.ipc.ClientConnection.waitForWork(Client.java:1017)

  • locked <0x000000042b52dbf0> (a org.apache.hadoop.ipc.Client$Connection)

at org.apache.hadoop.ipc.ClientConnection.run(Client.java:1061)

Locked ownable synchronizers:

  • None
相关推荐
虾球xz几秒前
游戏引擎学习第126天
java·学习·游戏引擎
郑祎亦17 分钟前
Java 关键字 volatile
java·开发语言·jvm
不会飞的小龙人20 分钟前
Quickwit获取Kafka数据源消息
java·docker·容器·kafka·quickwit
BTU_YC31 分钟前
Failed to start The PHP FastCGI Process Manager.
开发语言·php
诗诗的博客37 分钟前
jmeter聚合报告如何添加单位_性能测试连载(8)jmeter压力测试中的难点解析
java·开发语言
luoluoal1 小时前
java项目之基于ssm的线上旅游体验系统(源码+文档)
java·mysql·mybatis·ssm·源码
xidianhuihui1 小时前
go如何排查某个依赖是哪里引入的
开发语言·后端·golang
怡~1 小时前
Golang适配达梦数据库连接指定模式
开发语言·数据库·golang
姜来可期1 小时前
【Golang】go语言异常处理快速学习
开发语言·笔记·后端·学习·golang
ianozo1 小时前
BUU40 [CSCCTF 2019 Qual]FlaskLight1【SSTI】
开发语言·python