[不懂就问]. 为啥使用Thread类创建线程start以后可以直接run呢

举个栗子:https://www.jianshu.com/p/79f5e0bcd652
线程调用start()方法后便会run()起来,但是为什么呢,加个断点debug一下来看看.

  • 首先执行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) {
                
            }
        }
    }
  • 方法首先判断threadStatus变量值,来看这个变量的声明
private volatile int threadStatus = 0;
//volatile是一个类型修饰符(type specifier).volatile的作用是作为指令[关键字],确保本条指令不会因[编译器]的优化而省略,且要求每次直接读值。

看名字应该是表示线程状态的一个状态,默认为0.非零的话抛出一个非法线程状态的异常.

  • 然后向ThreadGroup的group添加当前线程对象.
  • 定义一个Boolean变量started标记
  • 然后执行start0方法,执行完以后控制台就已经有输出了
    看来关键就是这个start0方法
    来看是声明
private native void start0();

(以下过程比较复杂,像我这样的初学者可以直接拖到问尾大致了解一下)
(以下部分涉及转载,原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-processthread/#icomments
)

  • Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(),stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的 . 这个方法放在一个 static 语句块中,这就表明,当该类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。
private static native void registerNatives(); 
 static{ 
      registerNatives(); 
 }
  • 本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,定义了各个操作系统平台都要用到的关于线程的公用数据和操作.
JNIEXPORT void JNICALL 
Java_Java_lang_Thread_registerNatives (JNIEnv *env, jclass cls){ 
  (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); 
} 
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}, 
};
  • 到此,可以容易的看出 Java 线程调用 start 的方法,实际上会调用到 JVM_StartThread 方法,那这个方法又是怎样的逻辑呢。实际上,我们需要的是(或者说 Java 表现行为)该方法最终要调用 Java 线程的 run 方法,事实的确如此。 在 jvm.cpp 中,有如下代码段:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) 
   …
    native_thread = new JavaThread(&thread_entry, sz); 
   …
  • 这里JVM_ENTRY是一个宏,用来定义JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程,其线程函数是 thread_entry.
static void thread_entry(JavaThread* thread, TRAPS) { 
   HandleMark hm(THREAD); 
    Handle obj(THREAD, thread->threadObj()); 
    JavaValue result(T_VOID); 
    JavaCalls::call_virtual(&result,obj, 
    KlassHandle(THREAD,SystemDictionary::Thread_klass()), 
    vmSymbolHandles::run_method_name(), 
vmSymbolHandles::void_method_signature(),THREAD); 
}
  • 可以看到调用了 vmSymbolHandles::run_method_name 方法,这是在 vmSymbols.hpp 用宏定义的:
class vmSymbolHandles: AllStatic { 
   …
    template(run_method_name,"run") 
   …
}
  • 至于 run_method_name 是如何声明定义的,因为涉及到很繁琐的代码细节,本文不做赘述。感兴趣的读者可以自行查看 JVM 的源代码。


    图一
  • 综上所述,Java 线程的创建调用过程如 图 1 所示,首先 , Java 线程的 start 方法会创建一个本地线程(通过调用 JVM_StartThread),该线程的线程函数是定义在 jvm.cpp 中的 thread_entry,由其再进一步调用 run 方法。可以看到 Java 线程的 run 方法和普通方法其实没有本质区别,直接调用 run 方法不会报错,但是却是在当前线程执行,而不会创建一个新的线程。

你可能感兴趣的:([不懂就问]. 为啥使用Thread类创建线程start以后可以直接run呢)