• 读懂jstack


    1 jstack

    jstack是JDK自带的一种线程栈跟踪工具,用于生成java虚拟机当前时刻线程快照。在定位线程卡顿、死锁、block等原因的时候非常有用。使用方法是:

    jstack [-l] pid

    2 Monitor

    Monitor是java中用以实现线程同步和互斥的主要手段,每一个对象有且只有一个monitor,可以理解monitor就是一个对象锁。用下面这张著名的图来描述线程和monitor的关系
    monitor

    • Entry Set中的线程正在通过synchronized要求获取到对象锁。如果获取到锁就会进度The Owner,没有获取到锁就会在Entry Set等待
    • The Owner中的线程成功竞争到锁
    • Wait Set表示线程调用wait方法,释放对象锁,并在Wait Set等待。当调用notify或者notifyAll后,处在Wait Set中的一个(notify)或者多个(notifyAll)线程就会被放到Entry Set中同其他没有获取到锁的线程一起竞争锁。

    其实调用wait方法可以分为三个操作:

    1. 释放锁并阻塞(放到monitor的Wati Set中)
    2. 等待条件发生(时间到或者调用了notify或者notifyAll)
    3. 获取通知后,竞争获取锁(放到Entry Set中)

    那么对于Jstack打印的线程栈来讲,处在EntrySet中的线程动作是"Waiting for monitor entry", 处在The Owner中的线程动作是“runnable”,处在Wait Set中的线程动作是“in Object.wait()”。

    3 线程栈内容

    网上有很多介绍jstack中线程状态的,但是讲解的比较乱,这里做下总结。
    不同的jvm可能打印出的线程栈是不同的,当前使用的java版本如下

    java version "1.8.0_111"
    Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
    

    如下jstack打印出的线程栈快照
    jstack

    3.1 线程动作

    线程动作在jstack中第一行,表示线程在做什么,个人理解起到概括的作用(网上很多把这块也当做线程状态,我觉得不太合适),之后才是真正的栈

    • runnable 正在运行
    • in Object.wait() 如上面介绍的,调用了wait方法,在Wait Set中
    • waiting for monitor entry, 在Entry Set,等待获取锁
    • waiting on condition 在等待某个状态的发生,如sleep,时间到后就会活跃,或者io阻塞

    3.2 线程状态

    第二行才是真正的线程状态,线程状态是在java.lang.Thread.State中已经定义了的(java.lang.Thread.State是个枚举类),我们可以看下java源码

    public enum State {
            /**
             * Thread state for a thread which has not yet started.
             */
            NEW,
    
            /**
             * Thread state for a runnable thread.  A thread in the runnable
             * state is executing in the Java virtual machine but it may
             * be waiting for other resources from the operating system
             * such as processor.
             */
            RUNNABLE,
    
            /**
             * Thread state for a thread blocked waiting for a monitor lock.
             * A thread in the blocked state is waiting for a monitor lock
             * to enter a synchronized block/method or
             * reenter a synchronized block/method after calling
             * {@link Object#wait() Object.wait}.
             */
            BLOCKED,
    
            /**
             * Thread state for a waiting thread.
             * A thread is in the waiting state due to calling one of the
             * following methods:
             * <ul>
             *   <li>{@link Object#wait() Object.wait} with no timeout</li>
             *   <li>{@link #join() Thread.join} with no timeout</li>
             *   <li>{@link LockSupport#park() LockSupport.park}</li>
             * </ul>
             *
             * <p>A thread in the waiting state is waiting for another thread to
             * perform a particular action.
             *
             * For example, a thread that has called <tt>Object.wait()</tt>
             * on an object is waiting for another thread to call
             * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
             * that object. A thread that has called <tt>Thread.join()</tt>
             * is waiting for a specified thread to terminate.
             */
            WAITING,
    
            /**
             * Thread state for a waiting thread with a specified waiting time.
             * A thread is in the timed waiting state due to calling one of
             * the following methods with a specified positive waiting time:
             * <ul>
             *   <li>{@link #sleep Thread.sleep}</li>
             *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
             *   <li>{@link #join(long) Thread.join} with timeout</li>
             *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
             *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
             * </ul>
             */
            TIMED_WAITING,
    
            /**
             * Thread state for a terminated thread.
             * The thread has completed execution.
             */
            TERMINATED;
        }
    

    可以看到java的线程状态一共是6种,分别是:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

    • BLOCKED 线程在等待获取锁,可以理解为在Entry Set中
    • WAITING 由于调用了以下方法线程会变为WAITING状态:wait join park
    • TIMED_WAITING 由于调用了以下放啊发线程会变为TIMED_WAITING:sleep wait(long) join(long)

    3.3 调用修饰

    • locked <地址> 获取到对象锁,monitor的y拥有者
    • wating to lock <地址> 在Entry Set等待获取锁
    • waiting on <地址> 获取到锁对象后,释放锁在Wait Set等待
    • parking to wait for <地址>

    3.4 其他

    • prio 线程优先级
    • tid 表示线程id
    • nid 操作系统映射的线程id
    • [0x0000700004843000] 表示线程栈的其实地址或者锁地址

    4. 举例

    使用jstack查看线程栈的时候应该从下往上看,如下:
    jstack举例
    为什么会即锁住一个对象,又在一个对象的Wait Set 等待呢? 其实我们从下往上按照时间线来看就说的通了,线程首先锁住了该对象,即locked <0x000000076abe6c00>(过去式),然后调用了wait,被放到了Wait Set中,即waiting on <0x000000076abe6c00>

    java类似如下:

    class Producer implements Runnable {
            private Queue<Integer> buffer;
    
            public Producer(Queue<Integer> buffer) {
                this.buffer = buffer;
            }
            @Override
            public void run(){
                while (true) {
                    synchronized (buffer) {
                        Thread.currentThread().setName("producer");
                        while (buffer.size() >= 10) {
                            try {
                                buffer.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        Integer time = new Random().nextInt(100);
                        try {
                            Thread.sleep(time);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("producer remove: " + time);
                        buffer.add(time);
                        buffer.notifyAll();
                    }
                }
            }
        }
    

    5. 参考

    http://www.cnblogs.com/kongzhongqijing/articles/3630264.html

  • 相关阅读:
    主从热备+负载均衡(LVS + keepalived)
    这12行代码分分钟让你电脑崩溃手机重启
    Linux 下虚拟机——Virtual Box
    软件著作权登记证书申请攻略
    ecshop整合discuz教程完美教程
    NetHogs——Linux下按进程实时统计网络带宽利用率
    深入研究CSS
    SSH远程会话管理工具
    nginx防止SQL注入规则
    mysql完美增量备份脚本
  • 原文地址:https://www.cnblogs.com/set-cookie/p/8727374.html
Copyright © 2020-2023  润新知