简述java线程类Thread的状态获取逻辑

Java线程有六种状态,通过内部的threadStatus属性和与运算来判断。线程状态在Thread.start()等方法调用时由JVM底层变更,如JVM_StartThread函数。线程状态的枚举值是JVMTI中的常量组合,通过与运算获取当前状态,如RUNNABLE是JVMTI_THREAD_STATE_ALIVE和JVMTI_THREAD_STATE_RUNNABLE的组合。
摘要由CSDN通过智能技术生成


前言

Thread是java并行场景实现中经常用到的类,它会在主线程运行同时再开启新的线程,两个或者多个线程同时运行,以提高程序的执行效率。应用到实际项目中可以大大提升响应时间,提升用户体验。
我们都知道java线程有的状态有六种:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,这六种状态在Thread中被定义为枚举,我这两天通过看java的源码以及JVM的部分源码发现它的状态变更的设计,不是说线程该到哪个值就把状态set成对应的枚举,下面我们来看下他的实现逻辑。


状态获取之-与运算

这里先给出答案-----就是与运算实现的。
下面根据源码一步一步的分析一下

public class ThreadStateExample {

    static class ThreadDemo extends Thread{
        @Override
        public void run() {
            System.out.println("hello world");
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new ThreadDemo());
        thread.start();
        thread.getState();
    }
}

如上代码,是我通过继承Thread类方式实例了一个Thread对象。通过代码可以看到thread有一个getState()的方法,见名知意,他是获取此线程对象的当前状态的,我也试过写thread.setState()的方法调用,但是Thread中没有这个方法,显然线程状态的变更不可能通过直接set的方式。那么我接着就去看了一下getState()的源码,我把相关的代码贴到下方

/* Java thread status for tools,
 * initialized to indicate thread 'not yet started'
 */

private volatile int threadStatus = 0;

/**
 * Returns the state of this thread.
 * This method is designed for use in monitoring of the system state,
 * not for synchronization control.
 *
 * @return this thread's state.
 * @since 1.5
 */
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

toThreadState(threadStatus)方法

public static Thread.State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

Thread类先初始化了一个threadStatus的属性,然后通过对此属性的与运输得到对应的线程状态。
那么现在有个问题:这个threadStatus的属性,在Thread类中既没有set方法,也没有get方法,他是如何变更的呢?
在解决这个问题之前,我们来想一下,java的线程什么时候会涉及到状态的变更,这应该是个容易的问题吧。
有几个场景,比如:Thread.start()、Object.sleep(long)、Object().wait(long)以及锁等待等等。而通过我扒源码发现这些方法都是有native关键字来修饰,也就是他们最终都是调用到JVM底层也就是通过C来实现的。这里有几点需要明白

  1. 在 Java 应用程序中,调用 Thread.start() 方法启动新线程。
  2. Java 虚拟机将 start() 方法调用转换为本地方法调用,并将参数传递给本地方法 start0()。
  3. 在 start0() 方法中,Java 虚拟机会创建一个新线程并将其绑定到一个操作系统线程。
  4. Java 虚拟机会设置新线程的执行入口为 Java 线程的 run() 方法,并将 Java 线程的 Thread 对象与操作系统线程关联起来。
  5. 当操作系统线程启动后,它会从 run() 方法的代码开始执行,并在执行完 run() 方法后退出。
  6. 在 Java 应用程序中,线程的状态会从 NEW 状态变为 RUNNABLE 状态,并且线程的 run() 方法会在新线程中被执行。

下面我来跟下start()方法的源码,来看下线程状态是如何发生变更的。start方法中调用了本地方法

private native void start0();

java中凡是用了native关键字修饰的类,都会有一个XXX.c的文件(java jni的函数库)与之对应,那么我找到JDK8的源码中Thread.c的文件,粘出相关代码如下;

Thread.c
static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

我到start0对应c的JVM_StartThread函数,这里就不再说c的文件怎么找了,大家可以自己研究一下,而JVM_StartThread函数调用链路涉及jvm.cpp、thread.cpp两个文件,粘出相关代码如下;

jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))

  //。。。函数实现太长,我这里省略其他代码,只看相关代码

  Thread::start(native_thread);

JVM_END
thread.cpp
void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    os::start_thread(thread);
  }
}

通过上面源码可以看到,线程状态的变更最终是通过java_lang_Thread::set_thread_status函数实现的,那么接着来看java_lang_Thread::RUNNABLE枚举值是什么,它是在javaClass.hpp文件中被定义的。粘出相关代码如下:

javaClass.hpp
enum ThreadStatus {
    NEW                      = 0,
    RUNNABLE                 = JVMTI_THREAD_STATE_ALIVE +          // runnable / running
                               JVMTI_THREAD_STATE_RUNNABLE,
    SLEEPING                 = JVMTI_THREAD_STATE_ALIVE +          // Thread.sleep()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_SLEEPING,
    IN_OBJECT_WAIT           = JVMTI_THREAD_STATE_ALIVE +          // Object.wait()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    IN_OBJECT_WAIT_TIMED     = JVMTI_THREAD_STATE_ALIVE +          // Object.wait(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    PARKED                   = JVMTI_THREAD_STATE_ALIVE +          // LockSupport.park()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_PARKED,
    PARKED_TIMED             = JVMTI_THREAD_STATE_ALIVE +          // LockSupport.park(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_PARKED,
    BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE +          // (re-)entering a synchronization block
                               JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
    TERMINATED               = JVMTI_THREAD_STATE_TERMINATED
};

到这里心里渐渐明朗,这些状态应该是几个常量值加起来之后得到一个数值。
等式右边这些枚举值是在jvmti.h文件中被定义的。

jvmti.h
enum {
    JVMTI_THREAD_STATE_ALIVE = 0x0001,
    JVMTI_THREAD_STATE_TERMINATED = 0x0002,
    JVMTI_THREAD_STATE_RUNNABLE = 0x0004,
    JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400,
    JVMTI_THREAD_STATE_WAITING = 0x0080,
    JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010,
    JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020,
    JVMTI_THREAD_STATE_SLEEPING = 0x0040,
    JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100,
    JVMTI_THREAD_STATE_PARKED = 0x0200,
    JVMTI_THREAD_STATE_SUSPENDED = 0x100000,
    JVMTI_THREAD_STATE_INTERRUPTED = 0x200000,
    JVMTI_THREAD_STATE_IN_NATIVE = 0x400000,
    JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000,
    JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000,
    JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000
};

这些值都是十六进制,那么我们算一下RUNNABLE状态值是多少。

RUNNABLE = JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE = 5;
JVMTI_THREAD_STATE_ALIVE = 0x0001 = 1;
JVMTI_THREAD_STATE_RUNNABLE = 0x0004 = 4;

上文中我们提到jvm创建的线程要和java线程对象关联,而这个状态值关联的就是threadStatus的属性。
最后根据java源码的与运输逻辑计算得出结果:

5 & 4
0101 & 0100 = 0100 =4

所以线程状态:return State.RUNNABLE;

总结

具体来说,Java 线程的状态是由多个状态标志位组合而成的,在获取线程状态时,通过与运算获取线程状态,会对这些二进制位进行操作,从而简单高效的得到线程的状态信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值