从JVM源代码的角度一步一步给你讲清楚线程状态的本质以及线程状态的由来

首先,需要明确的是java线程有6中状态Thread.State:
(1)初始(NEW):新创建了一个线程对象,但还没有调用start()方法;
(2)运行(RUNNABLE): java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”,线程对象创建后,其他线程调用了该对选哪个的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取Cpu的使用功能权限,此时处于就绪状态(Ready);就绪状态的线程在获得CPU时间片后变为运行中状态(running);
(3)阻塞(BLOCKED):表示线程阻塞于锁;
(4)等待(WAITING):进入该状态的线程需要等待其他线程做出一些特动作;
(5)超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自动返回;
(6)终止(TERMINATED):表示该线程已经执行完毕。

除了以上六种状态外, Thread还有一个成员变量叫threadStatus

private volatile int threadStatus;

那这个threadStatus的值是怎么来的,你知道吗?是否就等于thread.state的某一个值呢?

我们先来了解一下java线程和OS线程关系,如下图所示:
在这里插入图片描述

从上图可知,java线程和OS线程的关系是一对一模型, 创建一个用户线程就要创建一个内核线程。

在JVM中,也用C++定义了一些Thread类,它们的继承结构如下,其中对于java层线程到JVM层主要相关的有java层的java.lang.Thread、javaThread和OSThread。
(1)java.lang.thread 属于java层的线程对象,每个java层对象都会在JVM中使用oop来表示,所以它也会在JVM中产生一个oop。
(2)Thread 是C++ 定义了线程基类,除了OSThread类,作为其他线程的基类,它包含了OSThread对象的指针。
(4)javaThread 是C++定义的线程类,我们在java层创建的线程对象会使用javaTHread对象来表示,它包含了指向线程的OOP的指针。属于中间类。
(5)OSThread 是C++ 定义的线程,它不与其他线程构成继承关系,它是JVM对不同操作系统的线程的统一抽象,它维护了操作系统的句柄,用于获取操作系统的线程。

JVM主要是用C++实现的,JVM定义的Thread(hotspot\src\share\vm\runtime\hread.hpp)的类继承结构如下:

class WorkerThread;

// Class hierarchy
// - Thread
//   - NamedThread
//     - VMThread
//     - ConcurrentGCThread
//     - WorkerThread
//       - GangWorker
//       - GCTaskThread
//   - JavaThread
//   - WatcherThread

另外还有一个重要的类OSThread不在这个继承关系粒,它以组合的方式被Thread类所使用:

protected:
  // OS data associated with the thread
  OSThread* _osthread;  // Platform-specific thread information

  // Thread local resource area for temporary allocation within the VM
  ResourceArea* _resource_area;
  ...

Java.lang.Thread、OSThread、javaThread、VMThread 之间关系:
(1)java.lang.Thread 是java语言里的线程类, 由这个java类创建instance都会:1映射到一个操作系统的OSthread;
(2)OSThread是JVM中C++定义的类,代表了JVM中对底层操作系统的osthread的抽象,它维护着实际操作系统创建的线程句柄handle,可以获取底层osthread的状态。
(3)javaThread 是JVM中C++定义的类,一个javaThread的instance代表了在jvm中的java.lang.Thread 的instance,它维护了线程的状态,并且维护一个指针指向java.lang.Thread创建的对象(oop)。它同时还维护了一个指针所对一个in的OSThread,来获取底层操作系统创建的osThread的状态。
(4)VMThread是JVM中C++定义的类,这个类和用户创建的线程无关,是JVM本身用来进行虚拟机操作的线程,比如GC。

线程的实际状态,jvm中起码有三种状态的变化:
一种是代表的java规范中的线程状态(java_thread->threadObj())
一种是标识的jvm关联的系统线程的状态(thread->osthread())
一种是jvm线程在做转换的一种状态,是vm自己的状态表示。

要弄白jvm中线程状态是如何变化的,就需要回到中间类javaThread中,看javaThread的成员变量threadaObj,其文件路径为hotspot\src
share\vm\runtime\thread.hpp,代码如下:

typedef void (*ThreadFunction)(JavaThread*, TRAPS);

class JavaThread: public Thread {
  friend class VMStructs;
 private:
  JavaThread*    _next;                          // The next thread in the Threads list
  oop            _threadObj;                     // The Java level thread object  

threadaObj很重要,threadaObj 实际指向的是java中线程对象(thread
object),c++中指针是如何指向java中线程对象呢,在javaThread中还有两个ThreadObj()方法,其源码如下:

 // Thread oop. threadObj() can be NULL for initial JavaThread
 // (or for threads attached via JNI)
 oop threadObj() const                          { return _threadObj; }
 void set_threadObj(oop p)                      { _threadObj = p; }

第一个是get方法,其作用就是获取C++中的指针, 另一个是set方法,其作用就是设置线程对象的指针。这种指向关系被称之为附加(attached)。

那javaThread是如何建立_threadObj和java线程的关系呢?首先我们先看下启动线程实例的本地方法JVM_StartThread。文件路径为src\share\vm\prims\jvm.cpp;

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
 ...
  JavaThread *native_thread = NULL;

    ...
    //创建中间线程javaThread.
      native_thread = new JavaThread(&thread_entry, sz);

    
      if (native_thread->osthread() != NULL) {
       //调用prepare方法进行附着
        native_thread->prepare(jthread);
      }
    }
  }

  ...
  //启动中间线程
  Thread::start(native_thread);

JVM_END

从源码中可以看出,首先定义了一个中间线程实例javaThread,然后通过调用prepare方法把jthread进行了attached,最后启动中间线程。我们接着来看下创建(new)中间线程javaThread都做了那些事。javaThread的构造器做了哪些工作,其文件路径为src\share\vm\runtime\thread.cpp中查看javaThread的实现:

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
{
...
//创建操作系统的内核线程
 os::create_thread(this, thr_type, stack_sz);
 }
 void Thread::start(Thread* thread) {
 ...
  os::start_thread(thread);
 }

从源码中可以看出,中间线程javaThread构造器中做了一件很重要的事情,即创建了一个操作系统的内核线程(create_thread),调用start方法启动中间线程javaThread。启动中间线程的时候也启动了操作系统的内核线程。

传递进来的java线程和中间线程javaThread线程的成员变量_ThreadObj的关联是如何关联的,既需要看prepare(…)方法都做了什么工作,文件路径为hotspot\src\share\vm\runtime\thread.cpp

void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) {

  assert(Threads_lock->owner() == Thread::current(), "must have threads lock");
 
  Handle thread_oop(Thread::current(),
                    JNIHandles::resolve_non_null(jni_thread));
  assert(InstanceKlass::cast(thread_oop->klass())->is_linked(),
    "must be initialized");
    
  set_threadObj(thread_oop());
  java_lang_Thread::set_thread(thread_oop(), this);

  if (prio == NoPriority) {
    prio = java_lang_Thread::priority(thread_oop());
    assert(prio != NoPriority, "A valid priority should be present");
  }

  // Push the Java priority down to the native thread; needs Threads_lock
  Thread::set_priority(this, prio);

  prepare_ext();
  Threads::add(this);
}

从源码中可以看到,涉及到了两种不同的地址,一种是java堆内存中的地址jni_thread,另个是C++的堆外内存地址,两种地址是不能直接使用的,使用使用 JNIHandles::resolve_non_null 对地址进行了解析转换,实际访问的还是jvm的堆内存线程对象。 接着使用set_threadObj()方法把线程对象设置到_threadObj成员变量中,这就建立的关联关系。

接着我们来看下操作系统的内核线程是怎么一回事,文件路径为hotspot\src\share\vm\runtime\thread.cpp

void Thread::start(Thread* thread) {
  trace("start", thread);
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    //启动内核线程
    os::start_thread(thread);
  }
}

从源码中可知,在方法中有一个启动内核线程的方法 os::start_thread(thread)。在hotspot\src\os目录下可以看到windows、linux。solaris和posix的实现,如下图:
在这里插入图片描述
以linux胃里,我们先检查liunx\vm\os_linux.cpp

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
...
//创建一条内核线程
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
...
}

pthread_create是POSIX中的API,创建一条操作系统线程。是在启动中间线程javaThread的时候才会创建操作系统内核线程。

接来敲黑板,考试重点来了, 线程状态是如何修改的,以javaThread::sleep(jlong millis)为例进行解释,这个过程是在C++中进行的。文件路径为hotspot\src\share\vm\prims\jvm.cpp,线程状态sleep状态转换的核心代码如下,

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
...
  //用于修饰线程状态并做一些统计
  //当睡眠结束后,会修改后回线程状态,在ThreadSleepState的析构函数中修改
  JavaThreadSleepState jtss(thread);
...
JVM_END

从代码可知,先定义一个局部变量jtss,传递了一个java线程thread参数来修改线程的状态,我们接着来看下JavaThreadSleepState构造方法,在这个构造方法中完成了什么工作。在状态格式化的时候,把sleeping的归类成为TIMED_WAITING(sleeping)。于此同时javaThread还拿着系统的线程thread->osthread()。文件路径为Hotspot\src\share\vm\services\threadService.hpp;构造方法的源码如下:

// Change status to sleeping
class JavaThreadSleepState : public JavaThreadStatusChanger {
 private:
  ThreadStatistics* _stat;
  bool _active;
 public:
  JavaThreadSleepState(JavaThread *java_thread) :
    JavaThreadStatusChanger(java_thread, java_lang_Thread::SLEEPING) {
    if (is_alive()) {
      _stat = java_thread->get_thread_stat();
      _active = ThreadService::is_thread_monitoring_contention();
      _stat->thread_sleep();
      if (_active) {
        _stat->thread_sleep_begin();
      }
    } else {
      _active = false;
    }
  }

  ~JavaThreadSleepState() {
    if (_active) {
      _stat->thread_sleep_end();
    }
  }
};


从源码可知JavaThreadSleepState继承了基类JavaThreadStatusChanger,在构造器之前调用了基类的修改状态的方法JavaThreadStatusChanger(…),java_thread为java线程,java_lang_Thread::SLEEPING为新的状态改为sleeping模式。我们再来看下基类的JavaThreadStatusChanger(…)构造器完成的工作,其文件路径为Hotspot\src\share\vm\services\threadService.hpp:

 JavaThreadStatusChanger(JavaThread* java_thread,
                          java_lang_Thread::ThreadStatus state) : _old_state(java_lang_Thread::NEW) {
    save_old_state(java_thread);
    set_thread_status(state);
  }

从代码可知,首先调用save_old_state(…)把旧的线程状态存起来,然后调用 set_thread_status(…)设置新的状态,set_thread_status(…)的方法实现如下:

  static void set_thread_status(JavaThread* java_thread,
                                java_lang_Thread::ThreadStatus state) {
    java_lang_Thread::set_thread_status(java_thread->threadObj(), state);
  }

从源码可知,调用set_thread_status(…)方法把java的线程对象threadObj拿出来,把状态state放进去,把java_thread->threadObj()的状态变为java_lang_Thread::SLEEPING。接着深入一下java_lang_Thread::set_thread_status方法实现,文件路径为Hotspot\src\share\vm\classfile\javaClasses.cpp,其源码实现如下:

// Write the thread status value to threadStatus field in java.lang.Thread java class.
void java_lang_Thread::set_thread_status(oop java_thread,
                                         java_lang_Thread::ThreadStatus status) {
  if (_thread_status_offset > 0) {
    java_thread->int_field_put(_thread_status_offset, status);
  }
}

从源码可知,根据操作java字段把java状态的偏移量拿出来,java状态的偏移量就是thread中的成员threadstatus(线程状态)的相对于它对象的头部偏移量,然后把状态state放进去,简单说就是C++中操作java的成员变量,把threadstatus的地址拿出来,把线程的state值放进去,把线程状态修改为sleeping。

在代码中用的是java_lang_Thread::SLEEPING常量,我们看下如何定义的,文件路径为:Hotspot\src\share\vm\classfile\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
  };

此处就是java线程状态值的最终来源,这些常量值是根据JVM自己定义的一组常量累加而来的,这组常量根据不同的JVM可能有不同的值,跟厂商有关。

JVMTI并不一定在所有的java虚拟机上都有实现,不同的虚拟机的实现也不尽相同,不过在一些主流的虚拟机中,比如sum、IBM 中都会提供标准的JVMTI实现, JVMTI 是一套本地代码接口,因此使用JVMTI需要我们与C/C++以及JNI 打交道。事实撒花姑娘,开发时一般采用建立一个Agent的方式来实现JVMTI,它使用功能JVMTI函数,设置一些回调函数,并从java虚拟机中得到当前额运行态信息,并做出自己的判断,最后还可能操作虚拟机的运行态。把Agent编译成一个动态链接库之后,我们就可以在java程序启动的时候来加载它(启动加载模式),也可以在java 5 之后使用运行时加载(活动加载模式)、

jvm在初始化的时候,还会把java_lang_Thread::SLEEPING 装到另外一组常量THREAD_STATUS_SLEEPING 中,文件路径为Hotspot\agent\src\share\classes\sum\jvm\hotspot\oops\OopUtilities

 private static void initThreadFields() {
    if (threadNameField == null) {
      SystemDictionary sysDict = VM.getVM().getSystemDictionary();
      InstanceKlass k = sysDict.getThreadKlass();
      threadNameField  = (OopField) k.findField("name", "Ljava/lang/String;");
      threadGroupField = (OopField) k.findField("group", "Ljava/lang/ThreadGroup;");
      threadEETopField = (LongField) k.findField("eetop", "J");
      threadTIDField = (LongField) k.findField("tid", "J");
      threadStatusField = (IntField) k.findField("threadStatus", "I");
      threadParkBlockerField = (OopField) k.findField("parkBlocker",
                                     "Ljava/lang/Object;");
      TypeDataBase db = VM.getVM().getTypeDataBase();
      THREAD_STATUS_NEW = db.lookupIntConstant("java_lang_Thread::NEW").intValue();
    

        THREAD_STATUS_RUNNABLE = db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
        THREAD_STATUS_SLEEPING = db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
        THREAD_STATUS_IN_OBJECT_WAIT = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
        THREAD_STATUS_IN_OBJECT_WAIT_TIMED = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
        THREAD_STATUS_PARKED = db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
        THREAD_STATUS_PARKED_TIMED = db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
        THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
        THREAD_STATUS_TERMINATED = db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();
     

      if (Assert.ASSERTS_ENABLED) {
        // it is okay to miss threadStatusField, because this was
        // introduced only in 1.5 JDK.
        Assert.that(threadNameField   != null &&
                    threadGroupField  != null &&
                    threadEETopField  != null, "must find all java.lang.Thread fields");
      }
    }
  }

jstack的状态,其实是java规范中的线程状态java_thread->threadObj(),这个对应的是java的线程状态,也就是我们jstack看到的状态。
在这里插入图片描述

通过jstack获取到java线程状态值是来自于java堆内存的threadStatus值,是在C++中进行定义的,具体的值跟虚拟机有关系,JVM中的threadStatus值是根据threadStatus做了转换而来的,在java.lang.Thread.java中进行转换,源码如下:

public State getState() {
        // get current thread state
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }

看下toThreadState(…)方法是如何转换的,文件路径为jdk\src\share\classes\sun\misc\VM.java。

 public static Thread.State toThreadState(int threadStatus) {
        if ((threadStatus & JVMTI_THREAD_STATE_RUNNABLE) != 0) {
            return RUNNABLE;
        } else if ((threadStatus & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) {
            return BLOCKED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) {
            return WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) {
            return TIMED_WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_TERMINATED) != 0) {
            return TERMINATED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_ALIVE) == 0) {
            return NEW;
        } else {
            return RUNNABLE;
        }
    }

从源码可知,根据threadStatus和不同JVM平台进行判断,所以线程状态的值不是thread.State值。

hotspot\src\share\vm\runtime\osThread.hpp中OSThread 线程的状态ThreadState:


enum ThreadState {
  ALLOCATED,                    // Memory has been allocated but not initialized
  INITIALIZED,                  // The thread has been initialized but yet started
  RUNNABLE,                     // Has been started and is runnable, but not necessarily running
  MONITOR_WAIT,                 // Waiting on a contended monitor lock
  CONDVAR_WAIT,                 // Waiting on a condition variable
  OBJECT_WAIT,                  // Waiting on an Object.wait() call
  BREAKPOINTED,                 // Suspended at breakpoint
  SLEEPING,                     // Thread.sleep()
  ZOMBIE                        // All done, but not reclaimed yet
};

osThread 的状态与javaThread的状态不一样,在线程状态变化的过程中osThread的状态也会进行修改,在对应的JVM_Sleep代码中对内核线程状态进行修改:

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
...
 if (millis == 0) {
    //睡眠时间为0时,根据jvm中的参数ConvertSleepToYield做不同的处理
    //表示是否将sleep操作转为yield操作
    //分别调用os::yield()、os::sleep(thread, millis, true)
    if (ConvertSleepToYield) {
      os::yield();
    } else {
       //通过thread->osthread()->get_state()获取OSThread对象,
       //并将其状态设置为SLEEPING等到sleep结束后设置回原来状态。
      //获取并保存线程的旧状态
      ThreadState old_state = thread->osthread()->get_state();
      //将线程的状态设置为SLEEPING
      thread->osthread()->set_state(SLEEPING);
      //调用系统级别的sleep方法,此时只会睡眠最小时间间隔
      os::sleep(thread, MinSleepInterval, false);
     //恢复线程状态
      thread->osthread()->set_state(old_state);
    }
...
JVM_END

从源码中可知,设置 thread->osthread()的状态为sleep。

到此为止,java线程的本质以及由来就说完了,你明白了?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
线程的生命周期分为5个状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。 1. 新建状态:当一个Thread类或其子类的对象被创建时,该线程就处于新建状态。此时,该线程还没有开始运行,也没有被分配到CPU资源。 2. 就绪状态:当线程调用start()方法后,线程进入就绪状态,表示该线程已经准备好被运行,但是还没有被分配到CPU资源。 3. 运行状态:当线程被分配到CPU资源后,线程进入运行状态,开始执行run()方法中的代码。 4. 阻塞状态:在某些情况下,线程可能会被阻塞,如等待某个条件的满足、等待输入或输出、等待锁等。在这些情况下,线程会进入阻塞状态,直到满足条件后才会进入就绪状态,等待CPU资源的分配。 5. 死亡状态:当线程的run()方法执行完毕或者调用了stop()方法时,线程就进入了死亡状态线程一旦进入死亡状态,就不能再转移到其他状态了。 从JVM角度理解多线程的管理方法,JVM主要通过线程调度器来控制线程的执行。线程调度器负责在多个线程之间分配可用的CPU时间片,并按照优先级决定哪个线程先执行。JVM还提供了一些线程控制的API,如Thread类中的sleep()、join()、yield()等方法,可以帮助开发者更好地控制线程的执行。此外,JVM还提供了一些线程同步的机制,如synchronized关键字、Lock接口等,可以帮助开发者编写线程安全的程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

弯_弯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值