Java中Thread是创建和执行的流程

CKeenJavaJava基础J2SE约 1732 字大约 6 分钟

作者:程序员CKeen
博客:http://ckeen.cnopen in new window

长期坚持做有价值的事!积累沉淀,持续成长,升维思考!希望把编码作为长期兴趣爱好😄


1.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的创建和执行过程。

2.JDK中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的方法了。

3. JVM中Thread的创建和执行过程

为了进一步搞清楚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目录。
  1. 我们找到Thread类的中native函数映射文件:thread.c(源码详细路径:jdk/share/native/java/lang/)
// jdk/share/native/java/lang/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的native方法映射到了JVM的JVM_StartThread方法,我们继续看一下该方法的实现

  1. 我们查到JVM_StartThread方法,可以在jvm.h的头文件中找到JVM_StartThread方法的定义,如下:
// hotspot的虚拟机实现:hotspot/src/share/vm/prims/jvm.h

JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);

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

// hotspot/src/share/vm/prims/jvm.cpp
 
JVM_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对象,根据其头文件描述,我们可以看到他主要有如下划分: 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的线程就可以等待系统的调度执行了。

总结一下实际执行过程:

Thread执行过程
Thread执行过程