多线程ThreadDump分析&&Java对象的锁池和等待池&&

2020-05-11  本文已影响0人  邵红晓

获取日志

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

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四种状态

线程关键状态分析

  1. java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
  2. java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
  1. 每一个对象都有且仅有一个 Monitor, 一个对象上的Monitor在某个时刻,只能被一个线程拥有,该线程就是 “ActiveThread”,而其它线程都是 “Waiting Thread”,分别在两个队列Entry ListWait Set里等候。在Entry List中等待的线程状态是Waiting for monitor entry,而在Wait Set中等待的线程状态是in Object.wait()

  2. 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;
  }

状态示例

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)

wait/sleep/yield/join 的区别

  1. wait 实例方法,暂停线程执行,调用需要先在同步方法中获取对象锁,调用之后会释放对象锁,只有在其它线程需要获取对象锁然后调用该实例方法notify被唤醒之后才能在被wait的地方接着执行
  2. sleep 静态方法,短时间暂停当前线程执行,作用在当前线程上,不会释放对象锁,也不需要在同步方法中调用,等超时之后自动唤醒notify接着执行
  3. yield 静态方法,释放当前线程cpu时间片,至于哪个线程会执行,取决于调度器,当前线程进入ready状态,随时可能再被调度
  4. join 实例方法,底层实现通过wait()方式实现,暂停主线程执行,等待子线程执行完,主线程再执行(自动唤醒notify)
    以上共同点:都会释放cpu时间片,释放cpu资源
    image.png

问题

sleep/yield/join方法位于Thread类中
wait/notify/notifyAll方法的位于Object类中,作用是实现线程间的协作,那么为什么不放在Thread类中?
在调用wait(), notify()或notifyAll()的时候,必须先获得锁,且状态变量须由该锁保护

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

上一篇 下一篇

猜你喜欢

热点阅读