Java线程的状态分析

2023-03-15 13:45:28 浏览数 (2)

本文将从源码角度分析Java线程的各种状态以及进入该状态所对应的操作。OpenJDK版本

代码语言:javascript复制
➜  jdk hg id
76072a077ee1  jdk-11 28

首先用jstack命令看下Java线程的状态及堆栈信息(删减了无用内容)

代码语言:javascript复制
"TEST_while(true)" #12 ... runnable  [0x00007f22439e2000]
   java.lang.Thread.State: RUNNABLE
 at java.lang.Thread.yield(java.base@11/Native Method)
 at test.ThreadStateTest$1.run(modules/ThreadStateTest.java:13)


"TEST_Thread.sleep(n)" #13 ... waiting on condition  [0x00007f22438e1000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
 at java.lang.Thread.sleep(java.base@11/Native Method)
 at test.ThreadStateTest$2.run(modules/ThreadStateTest.java:22)


"TEST_Object.wait()" #14 ... in Object.wait()  [0x00007f22437e0000]
   java.lang.Thread.State: WAITING (on object monitor)
 at java.lang.Object.wait(java.base@11/Native Method)
 - waiting on <0x0000000718aeb7a0> (a test.ThreadStateTest$3)
 at java.lang.Object.wait(java.base@11/Object.java:328)
 at test.ThreadStateTest$3.run(modules/ThreadStateTest.java:34)
 - waiting to re-lock in wait() <...> (a test.ThreadStateTest$3)


"TEST_Object.wait(n)" #15 ... in Object.wait()  [0x00007f22436df000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
 at java.lang.Object.wait(java.base@11/Native Method)
 - waiting on <0x0000000718aec768> (a test.ThreadStateTest$4)
 at test.ThreadStateTest$4.run(modules/ThreadStateTest.java:47)
 - waiting to re-lock in wait() <...> (a test.ThreadStateTest$4)


"TEST_LockSupport.park()" #16 ... waiting on condition  [0x00007f22435de000]
   java.lang.Thread.State: WAITING (parking)
 at jdk.internal.misc.Unsafe.park(java.base@11/Native Method)
 at java.util.concurrent.locks.LockSupport.park(...)
 at test.ThreadStateTest$5.run(modules/ThreadStateTest.java:58)


"TEST_LockSupport.parkNanos(n)" #17 ... waiting on condition  [...]
   java.lang.Thread.State: TIMED_WAITING (parking)
 at jdk.internal.misc.Unsafe.park(java.base@11/Native Method)
 at java.util.concurrent.locks.LockSupport.parkNanos(...)
 at test.ThreadStateTest$6.run(modules/ThreadStateTest.java:65)


"TEST_synchronized" #18 ... waiting for monitor entry  [0x00007f22433dc000]
   java.lang.Thread.State: BLOCKED (on object monitor)
 at test.ThreadStateTest$7.run(modules/ThreadStateTest.java:74)
 - waiting to lock <...> (a java.lang.Class for java.lang.Object)

由上我们可以看到,Java线程的状态可以为RUNNABLE、WAITING、TIMED_WAITING和BLOCKED等,而通过其堆栈信息,我们又可以大致推测出是何种操作导致的这种状态。

不过,细心的朋友在看上面的堆栈信息时肯定会有很多困惑,比如,同样是调用了Object.wait方法,为什么线程#14进入的是WAITING状态,而线程#15进入的是TIMED_WAITING状态呢?

下面我们还是通过源码角度对此做个全面的分析。

首先,找到jstack命令对应的JVM源码

C 文件src/hotspot/share/services/attachListener.cpp

代码语言:javascript复制
static jint thread_dump(AttachOperation* op, outputStream* out) {
  ...
  // thread stacks
  VM_PrintThreads op1(out, print_concurrent_locks, print_extended_info);
  VMThread::execute(&op1);


  // JNI global handles
  VM_PrintJNI op2(out);
  VMThread::execute(&op2);


  // Deadlock detection
  VM_FindDeadlocks op3(out);
  VMThread::execute(&op3);


  return JNI_OK;
}

因为我们关心的是 thread stacks 部分,所以看下VM_PrintThreads对应的操作

C 文件src/hotspot/share/runtime/vm_operations.cpp

代码语言:javascript复制
void VM_PrintThreads::doit() {
  Threads::print_on(_out, true, false, _print_concurrent_locks, _print_extended_info);
}

再看下Threads::print_on方法

C 文件src/hotspot/share/runtime/thread.cpp

代码语言:javascript复制
// Threads::print_on() is called at safepoint by VM_PrintThreads operation.
void Threads::print_on(outputStream* st, bool print_stacks,
                       bool internal_format, bool print_concurrent_locks,
                       bool print_extended_info) {
  ...
  ALL_JAVA_THREADS(p) {
    ResourceMark rm;
    p->print_on(st, print_extended_info);
    if (print_stacks) {
      if (internal_format) {
        p->trace_stack();
      } else {
        p->print_stack_on(st);
      }
    }
    ...
  }
  ...
}

重点看下Java线程的print_on方法

C 文件src/hotspot/share/runtime/thread.cpp

代码语言:javascript复制
// Called by Threads::print() for VM_PrintThreads operation
void JavaThread::print_on(outputStream *st, bool print_extended_info) const {
  ...
  st->print_raw(get_thread_name());
  ...
  if (thread_oop != NULL) {
    st->print_cr("   java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop));
  }
  ...
}

在这里我们找到了输出线程状态的地方。我们再看下java_lang_Thread::thread_status_name方法

C 文件src/hotspot/share/classfile/javaClasses.cpp

代码语言:javascript复制
const char* java_lang_Thread::thread_status_name(oop java_thread) {
  assert(_thread_status_offset != 0, "Must have thread status");
  ThreadStatus status = (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset);
  switch (status) {
    case NEW                      : return "NEW";
    case RUNNABLE                 : return "RUNNABLE";
    case SLEEPING                 : return "TIMED_WAITING (sleeping)";
    case IN_OBJECT_WAIT           : return "WAITING (on object monitor)";
    case IN_OBJECT_WAIT_TIMED     : return "TIMED_WAITING (on object monitor)";
    case PARKED                   : return "WAITING (parking)";
    case PARKED_TIMED             : return "TIMED_WAITING (parking)";
    case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)";
    case TERMINATED               : return "TERMINATED";
    default                       : return "UNKNOWN";
  };
}

在该方法里,我们可以看到Java线程的所有可能状态,以及各种状态对应到jstack里会输出的名字。

那这些状态又是在哪里设置的呢?

JVM中有一个C 类叫JavaThreadStatusChanger,该类及其子类就是用来修改Java线程状态的,我们看下都有哪些子类

代码语言:javascript复制
File: src/hotspot/share/services/threadService.hpp
469:34:class JavaThreadInObjectWaitState : public JavaThreadStatusChanger {
498:28:class JavaThreadParkedState : public JavaThreadStatusChanger {
527:43:class JavaThreadBlockedOnMonitorEnterState : public JavaThreadStatusChanger {
584:27:class JavaThreadSleepState : public JavaThreadStatusChanger {

由上我们可以看到,一共有四个子类负责修改Java线程的状态。

但又是什么操作导致这些类对Java线程的状态进行修改的呢?下面我们以JavaThreadInObjectWaitState类举例说明下。首先看下这个类

C 文件src/hotspot/share/services/threadService.hpp

代码语言:javascript复制
// Change status to waiting on an object  (timed or indefinite)
class JavaThreadInObjectWaitState : public JavaThreadStatusChanger {
 ...
 public:
  JavaThreadInObjectWaitState(JavaThread *java_thread, bool timed) :
    JavaThreadStatusChanger(java_thread,
                            timed ? java_lang_Thread::IN_OBJECT_WAIT_TIMED : java_lang_Thread::IN_OBJECT_WAIT) {
    ...
  }
  ...
};

由上可见,该类会根据timed参数来决定是把Java线程修改为IN_OBJECT_WAIT_TIMED状态还是IN_OBJECT_WAIT状态。我们再来看下这个类是在哪里创建的,以及timed参数又是怎么传入的

C 文件src/hotspot/share/prims/jvm.cpp

代码语言:javascript复制
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
  ...
  JavaThreadInObjectWaitState jtiows(thread, ms != 0);
  ...
  ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END

由上可见,JavaThreadInObjectWaitState类的是由JVM_MonitorWait方法创建的,且timed参数是由ms是否等于0决定的。如果ms等于0,Java线程就会被JavaThreadInObjectWaitState类修改为IN_OBJECT_WAIT状态,如果不等于0,就会被修改为IN_OBJECT_WAIT_TIMED状态。

我们再来看下是谁调用的JVM_MonitorWait方法。

C文件src/java.base/share/native/libjava/Object.c

代码语言:javascript复制
static JNINativeMethod methods[] = {
    ...
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    ...
};

由上可以看到,是Java类java.lang.Object中的Object.wait这个native方法。

至此,我们就明白了,当我们调用Object.wait方法时,会切换Java线程的状态。如果调用该方法时传入的参数为0,Java线程就会被切换成IN_OBJECT_WAIT状态,对应jstack的输出为”WAITING (on object monitor)”。如果传入的参数大于0,Java线程就会被切换成IN_OBJECT_WAIT_TIMED状态,对应的jstack输出就是 “TIMED_WAITING (on object monitor)”。

其他三个子类也是类似,在此就不一一赘述了。

下面我们总结下,jstack命令可能输出线程状态以及导致这种状态的操作是什么。

jstack输出 | 对应的操作 ------------------------------------------------------------------------- NEW | 创建Thread对象 RUNNABLE | 调用Thread.start方法 TERMINATED | 销毁Thread对象 TIMED_WAITING (sleeping) | 调用Thread.sleep方法 WAITING (on object monitor) | 调用Object.wait方法,参数为0 TIMED_WAITING (on object monitor) | 调用Object.wait方法,参数大于0 WAITING (parking) | 调用Unsafe.park方法,参数为0 | (通常由LockSupport.park系列方法触发) TIMED_WAITING (parking) | 调用Unsafe.park方法,参数大于0 | (通常由LockSupport.park系列方法触发) BLOCKED (on object monitor) | 等待进入synchronize代码块 | (其他线程正在执行该代码块)

其实Java类java.lang.Thread.State的Javadoc对此也有较为详细的描述,只是其状态和jstack中的状态不是一一对应的,所以我们一开始并没有提到,这里也顺便看下吧。

代码语言:javascript复制
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 {@code Object.wait()}
     * on an object is waiting for another thread to call
     * {@code Object.notify()} or {@code Object.notifyAll()} on
     * that object. A thread that has called {@code Thread.join()}
     * 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;
}

最后,附上本文中用于jstack命令的Java测试代码

代码语言:javascript复制
// RUNNABLE
new Thread("TEST_while(true)") {
  @Override
  public void run() {
    while (true) {
      Thread.yield();
    }
  }
}.start();


// TIMED_WAITING (sleeping)
new Thread("TEST_Thread.sleep(n)") {
  @Override
  public void run() {
    try {
      Thread.sleep(Integer.MAX_VALUE);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}.start();


// WAITING (on object monitor)
new Thread("TEST_Object.wait()") {
  @Override
  public void run() {
    try {
      synchronized (this) {
        wait();
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}.start();


// TIMED_WAITING (on object monitor)
new Thread("TEST_Object.wait(n)") {
  @Override
  public void run() {
    try {
      synchronized (this) {
        wait(Integer.MAX_VALUE);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}.start();


// WAITING (parking)
new Thread("TEST_LockSupport.park()") {
  @Override
  public void run() {
    LockSupport.park();
  }
}.start();


// TIMED_WAITING (parking)
new Thread("TEST_LockSupport.parkNanos(n)") {
  @Override
  public void run() {
    LockSupport.parkNanos(Long.MAX_VALUE);
  }
}.start();


// BLOCKED (on object monitor)
synchronized (Object.class) {
  new Thread("TEST_synchronized") {
    @Override
    public void run() {
      synchronized (Object.class) {
        System.out.println("Should not reach here!");
      }
    }
  }.start();


  try {
    Thread.sleep(Integer.MAX_VALUE);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

0 人点赞