• Java线程状态,thread dump及分析


    线程状态机:

    • New:所有新建且还没有开始运行的线程,都处于此状态,意味着其代码还没开始执行,也没有开始调度。
    • Runnable:准备好运行的线程都处于此状态,这些线程在任意时刻可能处于等待运行或者是运行中,具体取决于线程调度器。线程调度器会为每个线程分配固定的运行时间,线程运行一段时间后将被暂停和交出CPU,让其他处于等待运行的线程有机会来运行。
    • Blocked/Waiting:线程暂时不活动时将进入此状态之一,例如等待I/O,访问已被加锁的临界区等。处于此状态的线程将不会继续运行且不消耗CPU,直到条件满足后,调度器重新把它的状态改为Runnable。
    • Timed Waiting:线程调用了sleep或者wait(time)的方法后,将进入此状态,直到超时或者受到通知。
    • Terminated:线程正常退出,遇到不寻常的错误或者没有处理的异常,都会进入此状态。处于此状态的线程不会再消耗CPU。
      线程各种状态的例子

    NewState

    package demo.thread.state;
    
    public class NewState {
    public static void main(String[] args) {
    Thread t = new Thread();
    System.out.println("Thread state: " + t.getState());
    }
    }
    

    输出:

    
    Thread state: NEW
    
    

    Runnable State

    package demo.thread.state;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static java.lang.Thread.yield;
    
    public class RunnableState {
    public static void main(String[] args) throws InterruptedException {
    List<Thread> threads = new ArrayList<>();
    int count = Runtime.getRuntime().availableProcessors() * 2;
    for (int i = 0; i < count; ++i) {
    Thread thread = new Thread(() -> {
    for (long j = 0; j < 10000000000000L; ++j) {
    if (j % 1000 == 0)
    yield();
    }
    });
    thread.setDaemon(true);
    threads.add(thread);
    }
    threads.forEach(Thread::start);
    for (int i = 0; i < 3; ++i) {
    if (i > 0)
    System.err.println("=================");
    Thread.sleep(1000);
    threads.forEach(thread -> System.err.println(thread.getName() + ": " + thread.getState()));
    }
    }
    }
    
    

    输出:

    Thread-0: RUNNABLE
    Thread-1: RUNNABLE
    Thread-2: RUNNABLE
    Thread-3: RUNNABLE
    Thread-4: RUNNABLE
    Thread-5: RUNNABLE
    Thread-6: RUNNABLE
    Thread-7: RUNNABLE
    =================
    Thread-0: RUNNABLE
    Thread-1: RUNNABLE
    Thread-2: RUNNABLE
    Thread-3: RUNNABLE
    Thread-4: RUNNABLE
    Thread-5: RUNNABLE
    Thread-6: RUNNABLE
    Thread-7: RUNNABLE
    =================
    Thread-0: RUNNABLE
    Thread-1: RUNNABLE
    Thread-2: RUNNABLE
    Thread-3: RUNNABLE
    Thread-4: RUNNABLE
    Thread-5: RUNNABLE
    Thread-6: RUNNABLE
    Thread-7: RUNNABLE
    

    Blocked State

    package demo.thread.state;
    
    public class BlockedState {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.err.println(threadName + ": Start to acquire lock...");
    synchronized (lock) {
    System.err.println(threadName + ": " + Thread.currentThread().getState());
    System.err.println(threadName + ": Lock acquired.");
    }
    });
    thread.setDaemon(true);
    
    synchronized (lock) {
    System.err.println(Thread.currentThread().getName() + ": Lock acquired.");
    thread.start();
    Thread.sleep(2000);
    System.err.println(thread.getName() + ": " + thread.getState());
    System.err.println(Thread.currentThread().getName() + ": Release Lock.");
    }
    thread.join();
    }
    }
    
    

    输出:

    
    main: Lock acquired.
    Thread-0: Start to acquire lock...
    Thread-0: BLOCKED
    main: Release Lock.
    Thread-0: RUNNABLE
    Thread-0: Lock acquired.
    

    Waiting State

    package demo.thread.state;
    
    public class WaitingState {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.err.println(threadName + ": Start to acquire lock...");
    synchronized (lock) {
    try {
    System.err.println(threadName + ": Before lock wait.");
    lock.wait();
    System.err.println(threadName + ": After lock wait.");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    });
    thread.setDaemon(true);
    
    thread.start();
    Thread.sleep(2000);
    System.err.println(thread.getName() + ": " + thread.getState());
    synchronized (lock) {
    System.err.println(Thread.currentThread().getName() + ": lock notify.");
    lock.notify();
    }
    Thread.sleep(1000);
    System.err.println(thread.getName() + ": " + thread.getState());
    thread.join();
    }
    }
    
    
    

    输出:

    
    
    Thread-0: Start to acquire lock...
    Thread-0: Before lock wait.
    Thread-0: WAITING
    main: lock notify.
    Thread-0: After lock wait.
    Thread-0: TERMINATED
    

    Timed Waiting State

    package demo.thread.state;
    
    public class TimedWaitingState {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.err.println(threadName + ": Start to acquire lock...");
    synchronized (lock) {
    try {
    System.err.println(threadName + ": Before lock wait.");
    lock.wait(1000);
    System.err.println(threadName + ": After lock wait timeout.");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    });
    thread.setDaemon(true);
    
    thread.start();
    Thread.sleep(500);
    System.err.println(thread.getName() + ": " + thread.getState());
    Thread.sleep(1000);
    System.err.println(thread.getName() + ": " + thread.getState());
    thread.join();
    }
    }
    
    
    
    
    Thread-0: Start to acquire lock...
    Thread-0: Before lock wait.
    Thread-0: TIMED_WAITING
    Thread-0: After lock wait timeout.
    Thread-0: TERMINATED
    

    Terminated State

    package demo.thread.state;
    
    public class TerminatedState {
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    System.err.println(Thread.currentThread().getName() + " is running.");
    });
    thread.start();
    thread.join();
    System.err.println(thread.getName() + ": " + thread.getState());
    }
    }
    

    输出:

    Thread-0 is running.
    Thread-0: TERMINATED
    

    获取Thread Dump
    查找java进程PID
    命令1:jps -l
    或者
    命令2:ps -ef | grep java
    注意:需要当前用户有足够权限
    通过 “man jps” 可以查看完整的命令参数

    获取Thread dump
    命令:jstack -l
    通过 “man jstack” 可以查看完整的命令参数

    获取进程内所有线程的信息
    命令:top -H -b -n1 -p $pid

    -H 表示top命令使用线程模式
    -b 表示用批处理
    -n 表示最大交互次数,因为我们指定了一个进程,所以设置为1
    -p 进程PID
    推荐每隔5s获取一次thread dump,总共获取5~10次,然后进行分析。
    以下为从eclipse收集来的脚本: jstackScript

    !/bin/bash
     Adaptation of script from eclipse.org http://wiki.eclipse.org/How_to_report_a_deadlock#jstackSeries_--_jstack_sampling_in_fixed_time_intervals_.28tested_on_Linux.29
    
    if [ $# -eq 0 ]; then
    echo >&2 "Usage: jstackSeries <pid> [ <count> [ <delay> ] ]"
    echo >&2 " Defaults: count = 10, delay = 1 (seconds)"
    exit 1
    fi
    
    pid=$1 # required
    count=${2:-10} # defaults to 10 times
    delay=${3:-1} # defaults to 1 second
    
    while [ $count -gt 0 ]
    do
    jstack $pid >jstack.$pid.$(date +%s.%N)
    top -H -b -n1 -p $pid >top.$pid.$(date +%s.%N)
    sleep $delay
    let count--
    echo -n "."
    done
    
    

    命令:./jstackScript.sh $PID 10 5

    Thread dump分析
    生成 thread dump的脚本:run

    !/bin/bash
    
    jps -l | grep $1 | awk '{print $1}' | xargs jstack -l > logs/$1.log
    

    命令:./run $className

    例子1:获取资源死锁

    package demo.thread.dump;
    
    public class DeadLock {
    public static void main(String[] args) throws InterruptedException {
    Object resourceA = new Object();
    Object resourceB = new Object();
    
    new Thread(() -> {
    synchronized (resourceA) {
    
    new Thread(() -> {
    synchronized (resourceB) {
    synchronized (resourceA) {
    }
    }
    }, "Thread-BBB").start();
    
    try {
    Thread.sleep(1000);
    } catch (Exception e) {
    }
    synchronized (resourceB) {
    }
    }
    }, "Thread-AAA").start();
    Thread.sleep(10000000);
    }
    }
    
    

    运行:./run DeadLock
    日志:

    "Thread-BBB" #11 prio=5 os_prio=0 tid=0x00007fb750001800 nid=0x4d12 waiting for monitor entry [0x00007fb78ca0c000]
    java.lang.Thread.State: BLOCKED (on object monitor)
    at demo.thread.dump.DeadLock.lambda$null$0(DeadLock.java:14)
    - waiting to lock <0x00000000d68273e8> (a java.lang.Object)
    - locked <0x00000000d68273f8> (a java.lang.Object)
    at demo.thread.dump.DeadLock$$Lambda$2/1465806831.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    
    "Thread-AAA" #10 prio=5 os_prio=0 tid=0x00007fb7a4259800 nid=0x4d11 waiting for monitor entry [0x00007fb78cb0d000]
    java.lang.Thread.State: BLOCKED (on object monitor)
    at demo.thread.dump.DeadLock.lambda$main$1(DeadLock.java:23)
    - waiting to lock <0x00000000d68273f8> (a java.lang.Object)
    - locked <0x00000000d68273e8> (a java.lang.Object)
    at demo.thread.dump.DeadLock$$Lambda$1/1078694789.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    
    Found one Java-level deadlock:
    =============================
    "Thread-BBB":
    waiting to lock monitor 0x00007fb744002178 (object 0x00000000d68273e8, a java.lang.Object),
    which is held by "Thread-AAA"
    "Thread-AAA":
    waiting to lock monitor 0x00007fb744003568 (object 0x00000000d68273f8, a java.lang.Object),
    which is held by "Thread-BBB"
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-BBB":
    at demo.thread.dump.DeadLock.lambda$null$0(DeadLock.java:14)
    - waiting to lock <0x00000000d68273e8> (a java.lang.Object)
    - locked <0x00000000d68273f8> (a java.lang.Object)
    at demo.thread.dump.DeadLock$$Lambda$2/1465806831.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    "Thread-AAA":
    at demo.thread.dump.DeadLock.lambda$main$1(DeadLock.java:23)
    - waiting to lock <0x00000000d68273f8> (a java.lang.Object)
    - locked <0x00000000d68273e8> (a java.lang.Object)
    at demo.thread.dump.DeadLock$$Lambda$1/1078694789.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Found 1 deadlock.
    

    JVM帮我们检测到了死锁:

    线程BBB已经锁定了 0x00000000d68273f8 (resourceB), 等待锁定 0x00000000d68273e8 (resourceA)
    线程AAA已经锁定了 0x00000000d68273e8 (resourceA),等待锁定 0x00000000d68273f8 (resourceB)
    线程AAA和BBB的状态都是BLOCKED
    例子2:ReentrantLock和共享对象的死锁

    package demo.thread.dump;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReentrantDeadLockWithShareObject {
    private static final Object sharedObject = new Object();
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
    public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
    lock.readLock().lock();
    
    new Thread(() -> {
    synchronized (sharedObject) {
    lock.writeLock().lock();
    }
    }, "Thread-BBB").start();
    
    try {
    Thread.sleep(1000);
    } catch (Exception e) {
    }
    synchronized (sharedObject) {
    }
    }, "Thread-AAA").start();
    
    Thread.sleep(100000000);
    }
    }
    
    
    
    "Thread-BBB" #11 prio=5 os_prio=0 tid=0x00007fdf44001800 nid=0x5474 waiting on condition [0x00007fdf8145e000]
    java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for <0x00000000d6831ea0> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:943)
    at demo.thread.dump.ReentrantDeadLockWithShareObject.lambda$null$0(ReentrantDeadLockWithShareObject.java:15)
    - locked <0x00000000d6828420> (a java.lang.Object)
    at demo.thread.dump.ReentrantDeadLockWithShareObject$$Lambda$2/1584062783.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    
    "Thread-AAA" #10 prio=5 os_prio=0 tid=0x00007fdf98232000 nid=0x5473 waiting for monitor entry [0x00007fdf8155f000]
    java.lang.Thread.State: BLOCKED (on object monitor)
    at demo.thread.dump.ReentrantDeadLockWithShareObject.lambda$main$1(ReentrantDeadLockWithShareObject.java:24)
    - waiting to lock <0x00000000d6828420> (a java.lang.Object)
    at demo.thread.dump.ReentrantDeadLockWithShareObject$$Lambda$1/558638686.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    

    这次没那么走运,JVM没帮我们检测出死锁。从日志可以看出:

    线程BBB已经锁定了 0x00000000d6828420(sharedObject),等待锁定 0x00000000d6831ea0
    线程AAA等待锁定 0x00000000d6828420 (sharedObject)
    线程BBB的状态是 WAITING
    线程AAA的状态是 BLOCKED
    例子3:繁忙的线程

    package demo.thread.dump;
    
    public class BusyRunning {
    public static void main(String[] args) throws InterruptedException {
    Thread busyThread = new Thread(() -> {
    for (long i = 0; i < 100000000000000L; ++i) {
    }
    }, "Thread-BUSY");
    busyThread.start();
    
    Thread.sleep(10000000);
    }
    }
    

    日志里面没有什么特别的东西。
    使用脚本来获取 top的输出:run_top

    !/bin/bash
    
    jps -l | grep $1 | awk '{print $1}' | xargs top -H -b -n1 -p > logs/$1.top
    
    

    命令:./run_top BusyRunning
    查看BusyRunning.top

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    22079 helowken 20 0 4680700 39560 16956 R 93.8 0.5 4:39.21 java
    22052 helowken 20 0 4680700 39560 16956 S 0.0 0.5 0:00.00 java
    22055 helowken 20 0 4680700 39560 16956 S 0.0 0.5 0:00.25 java
    22056 helowken 20 0 4680700 39560 16956 S 0.0 0.5 0:00.00 java
    

    发现 PID=22079的线程CPU耗时最高,把22079转成16进制:
    22079 / 16 = 1379 余 15 (F)
    1379 / 16 = 86 余 3
    86 / 16 = 5 余 6
    5 / 16 = 0 余 5
    所以22079的16进制就是 563F

    命令:grep -i “563F” logs/BusyRunning.log -A10

    "Thread-BUSY" #10 prio=5 os_prio=0 tid=0x00007fd56427f800 nid=0x563f runnable [0x00007fd54284c000]
    java.lang.Thread.State: RUNNABLE
    at demo.thread.dump.BusyRunning.lambda$main$0(BusyRunning.java:6)
    at demo.thread.dump.BusyRunning$$Lambda$1/1831932724.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    

    能看出繁忙线程正在运行的代码:
    at demo.thread.dump.BusyRunning.lambda$main$0(BusyRunning.java:6)

    例子4:无限等待的I/O

    package demo.thread.dump;
    
    public class WaitingForIO {
    public static void main(String[] args) throws InterruptedException {
    Thread ioThread = new Thread(() -> {
    try {
    while (System.in.read() != -1) {
    }
    } catch (Exception e) {
    }
    }, "IORunning");
    ioThread.start();
    ioThread.join();
    }
    }
    

    日志:

    "IORunning" #10 prio=5 os_prio=0 tid=0x00007f50002ae000 nid=0x5d82 runnable [0x00007f4fddd49000]
    java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:255)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
    - locked <0x00000000d671bed0> (a java.io.BufferedInputStream)
    at demo.thread.dump.WaitingForIO.lambda$main$0(WaitingForIO.java:7)
    at demo.thread.dump.WaitingForIO$$Lambda$1/1831932724.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
    
    Locked ownable synchronizers:
    - None
    

    不管等多久,thread dump多少次,IO线程的状态永远是Runnable,但是永远结束不了。

    Java LockSupport
    最后看一下Java提供的 LockSupport,可以替代已经废弃的suspend和resume方法,但绝大多数时候,都不会直接使用。

    LockSupport提供的park方法,用于暂停当前线程的调度;unpark方法用于给暂停的线程颁发一个允许,使其可以恢复运行。但是处于暂停的线程也可以被中断,所以在中断后需要判断条件是否已经满足,如果不满足,需要重新调用park方法来暂停。

    例子1:park和unpark的使用

    package demo.thread.lockSupport;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class LockSupportSimpleTest {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println("Park AAA");
    LockSupport.park(lock);
    System.out.println(threadName + " exits.");
    }, "AAA");
    t.start();
    
    Thread t2 = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println("Park BBB");
    LockSupport.park(lock);
    System.out.println(threadName + " exits.");
    }, "BBB");
    t2.start();
    
    Thread.sleep(1000);
    System.out.println("Blocker of AAA equals to lock: " + (LockSupport.getBlocker(t) == lock));
    System.out.println("Blocker of BBB equals to lock: " + (LockSupport.getBlocker(t2) == lock));
    
    System.out.println("AAA status: " + t.getState());
    System.out.println("BBB status: " + t2.getState());
    
    System.out.println("unpark AAA");
    LockSupport.unpark(t);
    System.out.println("unpark BBB");
    LockSupport.unpark(t2);
    
    t.join();
    t2.join();
    }
    }
    
    

    输出:

    Park AAA
    Park BBB
    Blocker of AAA equals to lock: true
    Blocker of BBB equals to lock: true
    AAA status: WAITING
    BBB status: WAITING
    unpark AAA
    unpark BBB
    AAA exits.
    BBB exits.
    

    例子2:有条件的等待
    以下取自Java Doc的例子

    package demo.thread.lockSupport;
    
    import java.util.Queue;
    import java.util.Random;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.locks.LockSupport;
    
    public class LockSupportConditionTest {
    private static AtomicBoolean locked = new AtomicBoolean(false);
    private static Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
    
    private static void lock() {
    boolean wasInterrupted = false;
    Thread current = Thread.currentThread();
    waiters.add(current);
    
    // Block while not first in queue or cannot acquire lock
    while (waiters.peek() != current ||
    !locked.compareAndSet(false, true)) {
    LockSupport.park();
    if (Thread.interrupted()) // ignore interrupts while waiting
    wasInterrupted = true;
    }
    
    waiters.remove();
    if (wasInterrupted) // reassert interrupt status on exit
    current.interrupt();
    }
    
    private static void unlock() {
    locked.set(false);
    LockSupport.unpark(waiters.peek());
    }
    
    public static void main(String[] args) throws InterruptedException {
    AtomicInteger count = new AtomicInteger(5);
    CountDownLatch startLatch = new CountDownLatch(count.get());
    for (int i = 0, len = count.get(); i < len; ++i) {
    Thread t = new Thread(() -> {
    try {
    Thread.sleep(new Random().nextInt(1000));
    } catch (InterruptedException e) {
    }
    startLatch.countDown();
    System.err.println(Thread.currentThread().getName() + " try to lock...");
    lock();
    System.err.println(Thread.currentThread().getName() + " locked successfully.");
    count.decrementAndGet();
    }, "TTT-" + i);
    t.start();
    }
    
    startLatch.await();
    System.err.println("Start to unlock...");
    while (count.get() > 0) {
    unlock();
    }
    System.err.println("Exits.");
    }
    }
    

    输出:

    TTT-4 try to lock...
    TTT-4 locked successfully.
    TTT-2 try to lock...
    TTT-0 try to lock...
    TTT-1 try to lock...
    TTT-3 try to lock...
    Start to unlock...
    TTT-2 locked successfully.
    TTT-0 locked successfully.
    TTT-1 locked successfully.
    TTT-3 locked successfully.
    Exits.
    

    jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式:
    jstack [-l][F] pid
    如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。进程处于hung死状态可以用-F强制打出stack。
    dump 文件里,值得关注的线程状态有:
    死锁,Deadlock(重点关注)
    执行中,Runnable
    等待资源,Waiting on condition(重点关注)
    等待获取监视器,Waiting on monitor entry(重点关注)
    暂停,Suspended
    对象等待中,Object.wait() 或 TIMED_WAITING
    阻塞,Blocked(重点关注)
    停止,Parked

    在摘了另一篇博客的三种场景:

    实例一:Waiting to lock 和 Blocked

    "RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd4f8684000]
       java.lang.Thread.State: BLOCKED (on object monitor)
    at org.apache.log4j.Category.callAppenders(Category.java:201)
    - waiting to lock <0x00000000acf4d0c0> (a org.apache.log4j.Logger)
    at org.apache.log4j.Category.forcedLog(Category.java:388)
    at org.apache.log4j.Category.log(Category.java:853)
    at org.apache.commons.logging.impl.Log4JLogger.warn(Log4JLogger.java:234)
    at com.tuan.core.common.lang.cache.remote.SpyMemcachedClient.get(SpyMemcachedClient.java:110)
    

    说明:
    1)线程状态是 Blocked,阻塞状态。说明线程等待资源超时!
    2)“ waiting to lock <0x00000000acf4d0c0>”指,线程在等待给这个 0x00000000acf4d0c0 地址上锁(英文可描述为:trying to obtain 0x00000000acf4d0c0 lock)。
    3)在 dump 日志里查找字符串 0x00000000acf4d0c0,发现有大量线程都在等待给这个地址上锁。如果能在日志里找到谁获得了这个锁(如locked < 0x00000000acf4d0c0 >),就可以顺藤摸瓜了。
    4)“waiting for monitor entry”说明此线程通过 synchronized(obj) {……} 申请进入了临界区,从而进入了下图1中的“Entry Set”队列,但该 obj 对应的 monitor 被其他线程拥有,所以本线程在 Entry Set 队列中等待。
    5)第一行里,"RMI TCP Connection(267865)-172.16.5.25"是 Thread Name 。tid指Java Thread id。nid指native线程的id。prio是线程优先级。[0x00007fd4f8684000]是线程栈起始地址。
    实例二:Waiting on condition 和 TIMED_WAITING

    "RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]
       java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)
    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)
    at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
    at java.lang.Thread.run(Thread.java:662)
    

    说明:

    1)“TIMED_WAITING (parking)”中的 timed_waiting 指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态;parking指线程处于挂起中。

    2)“waiting on condition”需要与堆栈中的“parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)”结合来看。首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue 并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。
    3)别的就看不出来了。

    实例三:in Obejct.wait() 和 TIMED_WAITING

    "RMI RenewClean-[172.16.5.19:28475]" daemon prio=10 tid=0x0000000041428800 nid=0xb09 in Object.wait() [0x00007f34f4bd0000]
       java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
    - locked <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
    at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516)
    at java.lang.Thread.run(Thread.java:662)
    

    说明:

    1)“TIMED_WAITING (on object monitor)”,对于本例而言,是因为本线程调用了 java.lang.Object.wait(long timeout) 而进入等待状态。

    2)“Wait Set”中等待的线程状态就是“ in Object.wait() ”。当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() ,“ Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到运行态。

    3)RMI RenewClean 是 DGCClient 的一部分。DGC 指的是 Distributed GC,即分布式垃圾回收。

    4)请注意,是先 locked <0x00000000aa672478>,后 waiting on <0x00000000aa672478>,之所以先锁再等同一个对象,请看下面它的代码实现:

    static private class  Lock { };
    private Lock lock = new Lock();
    public Reference<? extends T> remove(long timeout)
    {
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            for (;;) {
                lock.wait(timeout);
                r = reallyPoll();
                ……
           }
    }
    

    即,线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0x00000000aa672478> );当执行到 lock.wait(timeout);,线程就放弃了 Monitor 的所有权,进入“Wait Set”队列(对应于 waiting on <0x00000000aa672478> )。
    5)从堆栈信息看,是正在清理 remote references to remote objects ,引用的租约到了,分布式垃圾回收在逐一清理呢。

    参考文章
    Lifecycle and States of a Thread in Java
    Take thread dumps from a JVM
    How to Analyze Java Thread Dumps

    原文链接:https://blog.csdn.net/helowken2/article/details/95060830

  • 相关阅读:
    前端工程师应该具备的三种思维
    7 个 Bootstrap 在线编辑器用于快速开发响应式网站
    js阻止浏览器的默认行为以及停止事件冒泡(用JQuery实现回车提交)
    JAVASCRIPT加密方法,JS加密解密综述(7种)
    JavaScript生成GUID的方法
    js判断是否为手机访问
    Jquery中parent()和parents()
    jQuery中ajax和post处理json的不同
    JQuery实现回车代替Tab键(按回车跳到下一栏)
    js中replace的用法
  • 原文地址:https://www.cnblogs.com/cfas/p/15963496.html
Copyright © 2020-2023  润新知