获取日志
jps 或 ps –ef | grep java (获取PID)
jstack pid | tee -a jstack.log(获取ThreadDump),tee 输出到文件中同时打印到屏幕上
top -H -p 139626 查看该进程中线程的cpu使用率和内存使用率
$ top -H -p 139626
top - 14:10:30 up 367 days, 23:23, 1 user, load average: 3.05, 3.33, 9.76
Threads: 103 total, 0 running, 103 sleeping, 0 stopped, 0 zombie
%Cpu(s): 14.1 us, 0.8 sy, 0.0 ni, 85.0 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 26385808+total, 85839312 free, 61438876 used, 11657990+buff/cache
KiB Swap: 0 total, 0 free, 0 used. 19674998+avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
139743 data 20 0 10.110g 2.637g 12868 S 1.3 1.0 2251:34 java
139744 data 20 0 10.110g 2.637g 12868 S 1.3 1.0 2251:35 java
dump信息
- 代码
public class ObjectWaiteDemo {
private static Object obj = new Object();
public static void main(String[] args){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
synchronized(obj) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
}
jps
jstack pid
- dump信息
需要倒着看,进入同步代码块需要获取对象锁 - locked <0x000000076b60dba0>,然后执行at java.lang.Object.wait(Object.java:502)方法,释放锁之后线程处于 - waiting on <0x000000076b60dba0>
in Object.wait()
2020-05-11 14:35:50
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):
"Service Thread" 11 daemon prio=9 os_prio=0 tid=0x000000002124a800 nid=0x9b68 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
【主线程】
"DestroyJavaVM" 13 prio=5 os_prio=0 tid=0x0000000004e17000 nid=0x3d54 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
【自定义线程】
"Thread-0" 12 prio=5 os_prio=0 tid=0x0000000021423000 nid=0x9810 in Object.wait() [0x000000002213f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b60dba0> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at shengsiyuan.jdk8.jvmTuning.ObjectWaiteDemo$1.run(ObjectWaiteDemo.java:14)
- locked <0x000000076b60dba0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
1. 线程名称:Thread-0;线程类型:daemon;优先级: 10,默认是5
2. JVM线程id:tid=0x0000000021423000 ,JVM内部线程的唯一标识(通过java.lang.Thread.getId()获取,通常用自增方式实现)
3. 对应系统线程id(NativeThread ID):nid=0x9810 ,和top命令查看的线程pid对应,不过一个是10进制,一个是16进制。(通过命令:top -H -p pid,可以查看该进程的所有线程信息)
4. 线程状态:in Object.wait()
5. 起始栈地址:[0x000000002213f000],对象的内存地址,通过JVM内存查看工具,能够看出线程是在哪儿个对象上等待
线程生命周期
线程状态 RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,NEW,TERMINATED
主要分析线程RUNNABLE,BLOCKED,WAITING,TIMED_WAITING四种状态
- RUNNABLE:线程处于运行或者等待cpu时间片运行阶段
- BLOCKED:等待获取对象锁
- WAITING:处在该线程的状态,正在等待某个事件的发生,只有特定的条件满足,才能获得执行机会。而产生这个特定的事件,通常都是另一个线程。也就是说,如果不发生特定的事件,那么处在该状态的线程一直等待,不能获取执行的机会
1. A线程调用了obj对象的obj.wait()方法,如果没有线程调用obj.notify或obj.notifyAll,那么A线程就没有办法恢复运行;
2. 如果A线程调用了LockSupport.park(),没有别的线程调用LockSupport.unpark(A),那么A没有办法恢复运行。
- TIMED_WAITING:线程进入了WAITING状态,一定要特定的事件发生才能恢复运行;而处在
TIMED_WAITING
的线程,如果特定的事件发生或者是时间流逝完毕,都会恢复运行
线程关键状态分析
- Wait on condition && in Object.wait()
The thread is either sleeping or waiting to be notified by another thread
- java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
- java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
- Waiting for Monitor Entry
The thread is waiting to get the lock for an object (some other thread may be holding the lock). This happens if two or more threads try to execute **synchronized code**. Note that the lock is always for an object and not for individual methods.
image.png - 如上图所示
每一个对象都有且仅有一个 Monitor, 一个对象上的Monitor在某个时刻,只能被一个线程拥有,该线程就是 “ActiveThread”,而其它线程都是 “Waiting Thread”,分别在两个队列
Entry List
和Wait Set
里等候。在Entry List
中等待的线程状态是Waiting for monitor entry
,而在Wait Set
中等待的线程状态是in Object.wait()
。Java每个对象上面都有锁池和等待池
src/share/vm/prims/jvm.cpp
src/share/vm/runtime/synchronizer.hpp
src/share/vm/runtime/objectMonitor.hpp
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
- _EntryList 锁池 线程状态是
Waiting for monitor entry
存放等待获取该对象锁的线程,参与该对象锁的竞争,然后该线程进入runnable状态 - _WaitSet 等待池 线程状态是
in Object.wait() || waiting on condition
存放所有在该对象上wait的线程,不参与该对象锁的竞争,等待唤醒(条件唤醒,超时自动唤醒),然后该线程进入runnable状态
状态示例
1.waiting for monitor entry 示例
这1分钟内会t2一直持有object的监视器,另t2线程无法执行处在BLOCKED状态等待获取对象锁
public class BlockedState {
private static Object object = new Object();
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run()
{
synchronized (object)
{
long begin = System.currentTimeMillis();
long end = System.currentTimeMillis();
// 让线程运行1分钟,会一直持有object的监视器
while ((end - begin) <= 1 * 60 * 1000) {
end = System.currentTimeMillis();
}
}
}
};
new Thread(task, "t1").start();
new Thread(task, "t2").start();
}
}
"t2" #13 prio=5 os_prio=0 tid=0x00000000216be000 nid=0x65f4 runnable [0x00000000223de000]
java.lang.Thread.State: RUNNABLE
at shengsiyuan.jdk8.jvmTuning.BlockedState$1.run(BlockedState.java:22)
- locked <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"t1" #12 prio=5 os_prio=0 tid=0x00000000216bd800 nid=0x5604 waiting for monitor entry [0x00000000222de000]
java.lang.Thread.State: BLOCKED (on object monitor)
at shengsiyuan.jdk8.jvmTuning.BlockedState$1.run(BlockedState.java:16)
- waiting to lock <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
2.in Object.wait() 示例
public class WaitingState {
private static Object object = new Object();
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
synchronized (object) {
// 进入等待的同时,会进入释放监视器
try {
object.wait();
Thread.sleep(1000*60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(task, "t1").start();
new Thread(task, "t2").start();
}
}
"t2" #13 prio=5 os_prio=0 tid=0x0000000020eff800 nid=0x86c4 in Object.wait() [0x0000000021bff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b60db50> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at shengsiyuan.jdk8.jvmTuning.WaitingState$1.run(WaitingState.java:21)
- locked <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"t1" #12 prio=5 os_prio=0 tid=0x0000000020f17000 nid=0x90e4 in Object.wait() [0x0000000021afe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b60db50> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at shengsiyuan.jdk8.jvmTuning.WaitingState$1.run(WaitingState.java:21)
- locked <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"t2" #13 prio=5 os_prio=0 tid=0x0000000021820000 nid=0x3b98 waiting for monitor entry [0x000000002253f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at shengsiyuan.jdk8.jvmTuning.WaitingState$1.run(WaitingState.java:13)
- waiting to lock <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"t1" #12 prio=5 os_prio=0 tid=0x000000002181f000 nid=0x9268 waiting on condition [0x000000002243f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at shengsiyuan.jdk8.jvmTuning.WaitingState$1.run(WaitingState.java:20)
- locked <0x000000076b60db50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
3.waiting on condition 示例
public class TimedWaitingState {
// java的显示锁,类似java对象内置的监视器
private static Lock lock = new ReentrantLock();
// 锁关联的条件队列(类似于object.wait)
private static Condition condition = lock.newCondition();
public static void main(String[] args)
{
Runnable task = new Runnable() {
@Override
public void run() {
// 加锁,进入临界区
lock.lock();
try {
System.out.println("进入临届区");
condition.await(5, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 解锁,退出临界区
lock.unlock();
System.out.println("退出临届区");
}
};
new Thread(task, "t1").start();
new Thread(task, "t2").start();
}
}
"t2" #13 prio=5 os_prio=0 tid=0x0000000021629000 nid=0x77bc waiting on condition [0x00000000224be000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076b616028> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2163)
at shengsiyuan.jdk8.jvmTuning.TimedWaitingState$1.run(TimedWaitingState.java:26)
at java.lang.Thread.run(Thread.java:748)
"t1" #12 prio=5 os_prio=0 tid=0x0000000021628000 nid=0x2240 waiting on condition [0x00000000223bf000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076b616028> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2163)
at shengsiyuan.jdk8.jvmTuning.TimedWaitingState$1.run(TimedWaitingState.java:26)
at java.lang.Thread.run(Thread.java:748)
- 分析:
ReentrantLock同sync用法一样,性能上也没有太大差距,t2线程获取对象锁,进入临届区之后调用await释放对象锁,t1线程过程和t2一样,最后状态都处于TIMED_WAITING 状态,等待condition.notify()或者超时唤醒
wait/sleep/yield/join 的区别
- thread状态转移图
-
wait
实例方法,暂停线程执行,调用需要先在同步方法中获取对象锁,调用之后会释放对象锁,只有在其它线程需要获取对象锁然后调用该实例方法notify被唤醒之后才能在被wait的地方接着执行 -
sleep
静态方法,短时间暂停当前线程执行,作用在当前线程上,不会释放对象锁,也不需要在同步方法中调用,等超时之后自动唤醒notify接着执行 -
yield
静态方法,释放当前线程cpu时间片,至于哪个线程会执行,取决于调度器,当前线程进入ready状态,随时可能再被调度 -
join
实例方法,底层实现通过wait()方式实现,暂停主线程执行,等待子线程执行完,主线程再执行(自动唤醒notify)
以上共同点:都会释放cpu时间片,释放cpu资源
image.png
问题
sleep/yield/join方法位于Thread类中
wait/notify/notifyAll方法的位于Object类中,作用是实现线程间的协作
,那么为什么不放在Thread类中?
在调用wait(), notify()或notifyAll()的时候,必须先获得锁,且状态变量须由该锁保护
- 简答:
wait/notify/notifyAll方法位于Object中,也就相当于所有类都包含这三个方法
这个和waite实现原理有关系,wait等待其实是对象锁,由于Java中的每一个对象都有一个内置的锁对象,自然所有的类都理应有wait/notify/notifyAll方法
join 代码
public class JoinTest implements Runnable{
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " start-----");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " end------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* join(long millis)方法的实现,可以看出join方法就是通过wait方法来将线程的阻塞,
* 如果join的线程还在执行,则将当前线程阻塞起来,直到join的线程执行完成,当前线程才能执行。
* 不过有一点需要注意,这里的join只调用了wait方法,却没有对应的notify方法,原因是Thread的start方法中做了相应的处理,
* 所以当join的线程执行完成以后,会自动唤醒主线程继续往下执行
* @param args
*/
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<5;i++) {
Thread test = new Thread(new JoinTest());
test.start();
//join方法的作用是父线程(主线程)等待子线程执行完成后再执行,换句话说就是将异步执行的线程合并为同步的线程。
// JDK中提供三个版本的join方法,其实现与wait方法类似,
// join()方法实际上执行的join(0),而join(long millis, int nanos)也与wait(long millis, int nanos)的实现方式一致
test.join();
}
//join 可以阻塞主线程,直到子线程都执行完
System.out.println("Finished~~~");
}
}
参考
https://juejin.im/post/5b31b510e51d4558a426f7e9#heading-8