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