> 文档中心 > Java中Thread是怎么执行的?

Java中Thread是怎么执行的?

我们知道java中执行一个线程分为两步:创建Thread和启动start

第一步:创建线程对象。继承Thread对象重写run方法,或者实现Runnable接口run方法并通过Thread的构造方法创建Thread对象,

第二步:调用Thread的start方法启动线程

按照上面两步操作后,就可以开启线程来执行任务了,如下:

package cn.ckeen.threadtest;import java.util.concurrent.atomic.AtomicInteger;/ * @description:线程创建方法 * @author: CKeen * @date: 2022/5/16 10:05 上午 */public class MainThread extends Thread {    public static AtomicInteger  count = new AtomicInteger(0);    public static void main(String[] args) { // 方式一: 继承Thread类,重写run方法 Thread thread = new MainThread(); thread.start(); // 实现runnable接口 Thread thread2 = new Thread(new Runnable() {     @Override     public void run() {  doWork();     } }); thread2.start();    }    @Override    public void run() { doWork();    }    static void doWork(){ while(count.get() < 1000){     int countVal = count.getAndIncrement();     System.out.println("Hello World! count:" + String.valueOf(countVal));     try {  Thread.sleep(1000L);     }catch (Exception ex){  ex.printStackTrace();     } }    }}

看到这里我们一定会很好奇,Thread创建和执行的过程是怎么样的呢?

下面我们就来一步一步跟踪Thread的创建和执行过程:

第一步,我们看下Thread对象的创建,我们通过Thread类跳转到JDK源码可以看到有以下几个构造函数

public Thread() {   init(null, null, "Thread-" + nextThreadNum(), 0);}public Thread(Runnable target) {   init(null, target, "Thread-" + nextThreadNum(), 0);}Thread(Runnable target, AccessControlContext acc) {   init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);}public Thread(ThreadGroup group, Runnable target) {   init(group, target, "Thread-" + nextThreadNum(), 0);}public Thread(String name) {   init(null, null, name, 0);}public Thread(ThreadGroup group, String name) {   init(group, null, name, 0);}public Thread(Runnable target, String name) {   init(null, target, name, 0);}public Thread(ThreadGroup group, Runnable target, String name) {    init(group, target, name, 0);}public Thread(ThreadGroup group, Runnable target, String name,    long stackSize) {    init(group, target, name, stackSize);}

这里实际干活的是Thread的init方法,我们继续跟到init 

private void init(ThreadGroup g, Runnable target, String name,    long stackSize, AccessControlContext acc,    boolean inheritThreadLocals) {    if (name == null) { throw new NullPointerException("name cannot be null");    }    this.name = name;    Thread parent = currentThread();    SecurityManager security = System.getSecurityManager();    if (g == null) { if (security != null) {     g = security.getThreadGroup(); } if (g == null) {     g = parent.getThreadGroup(); }    }    g.checkAccess();    if (security != null) { if (isCCLOverridden(getClass())) {     security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); }    }    g.addUnstarted();    this.group = g;    this.daemon = parent.isDaemon();    this.priority = parent.getPriority();    if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader();    else this.contextClassLoader = parent.contextClassLoader;    this.inheritedAccessControlContext =     acc != null ? acc : AccessController.getContext();    this.target = target;    setPriority(priority);    if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals =  ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);    this.stackSize = stackSize;    tid = nextThreadID();}

init方法实际只是初始化了Thread的一个参数,比如thread的分组group,target设置,堆栈大小,tid等,到这里Thread对象就已经创建完成

第二步,我们继续看一下Thread对象的启动方法start:

public synchronized void start() {    if (threadStatus != 0) throw new IllegalThreadStateException();    group.add(this);    boolean started = false;    try { start0(); started = true;    } finally { try {     if (!started) {  group.threadStartFailed(this);     } } catch (Throwable ignore) { }    }}private native void start0();

跳转到start方法,可以看到start方法调用了start0,而start0方法是个native方法,实际干活的虚拟机对外提供的native的方法了。

为了进一步搞清楚Thread是怎么启动的,必须得知道start0方法都干了些什么,所以我们要跟踪到JVM的实现部分源码。这里我们基于openjdk版本jdk8-b120版本的进行研究,具体的源码可以自己去github的openjdk拉取。

下面我们具体来分析一下JDK对start0实现:

1. 打开源码目录,首先我们可以看到代码目录结构

我们只分析源码部分,不实际编译实际JDK的代码,所以我们主要关注两个以下两个目录:

  • jni函数映射目录jdk/src/share/native/java/,该目录下有jdk的native函数到JVM函数的映射定义。
  • jvm实现目录hotspot/src/share/vm, 等一会我们会涉及到该目录下的prims目录和runtime目录。

2. 我们找到Thread类的中native函数映射文件:thread.c(源码详细路径:jdk/share/native/java/lang/)

// jdk/share/native/java/lang/thread.cstatic 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的native方法映射到了JVM的JVM_StartThread方法,我们继续看一下该方法的实现

2. 我们查到JVM_StartThread方法,可以在jvm.h的头文件中找到JVM_StartThread方法的定义,如下:

// hotspot的虚拟机实现:hotspot/src/share/vm/prims/jvm.hJNIEXPORT void JNICALLJVM_StartThread(JNIEnv *env, jobject thread);

 进一步我们可以找到jvm.cpp代码的对JVM_StartThread方法的实现,如下:

// hotspot/src/share/vm/prims/jvm.cppJVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))  JVMWrapper("JVM_StartThread");  JavaThread *native_thread = NULL;  bool throw_illegal_thread_state = false;  {    MutexLocker mu(Threads_lock);    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {      throw_illegal_thread_state = true;    } else {      jlong size =      java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));      native_thread = new JavaThread(&thread_entry, sz);      if (native_thread->osthread() != NULL) { native_thread->prepare(jthread);      }    }  }  if (throw_illegal_thread_state) {    THROW(vmSymbols::java_lang_IllegalThreadStateException());  }  assert(native_thread != NULL, "Starting null thread?");  if (native_thread->osthread() == NULL) {    delete native_thread;    if (JvmtiExport::should_post_resource_exhausted()) {      JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, "unable to create new native thread");    }    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),"unable to create new native thread");  }  Thread::start(native_thread);JVM_END

这里除了做一些线程对象的初始化工作外,主要有两步操作:

1. native_thread = new JavaThread(&thread_entry, sz);创建一个JavaThread对象

2. 使用Thread::start(native_thread) 启动线程

而JavaThread的对象,实际位于hotspot的runtime的下thread.cpp文件,该文件主要定义了JVM中使用的Thread对象,根据其头文件描述,我们可以看到他主要有如下划分:

 而JavaThread是hotspot的JVM实现的,对应于java用户定义的Thread对象的实际对象。jvm.cpp中的调用的JavaThread的构造函数定义如下:

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :  Thread()#if INCLUDE_ALL_GCS  , _satb_mark_queue(&_satb_mark_queue_set),  _dirty_card_queue(&_dirty_card_queue_set)#endif // INCLUDE_ALL_GCS{  if (TraceThreadEvents) {    tty->print_cr("creating thread %p", this);  }  initialize();  _jni_attach_state = _not_attaching_via_jni;  set_entry_point(entry_point);  // Create the native thread itself.  // %note runtime_23  os::ThreadType thr_type = os::java_thread;  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :    os::java_thread;  os::create_thread(this, thr_type, stack_sz);  _safepoint_visible = false;}

源码中我们可以看到在创建JavaThread的对象的时候,使用os::create_thread方法创建了一个系统线程,该线程对应于系统的内核线程,最终实际干活的为该线程。我们经常说java线程和实际执行的内核线程配比为1:1,应该就是对应于每个JavaThread的都创建了一个系统线程的与其对应。

JavaThread的对象创建完成,也创建了一个os的thread预期对应,接着调用了Thread::start(native_thread)的方法,启动线程,我们找到Thread的::start方法如下:

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);  }}

这里Thread的start方法直接调用os::start_thread启动了系统线程,到此java的线程就可以等待系统的调度执行了。

总结一下实际执行过程: