欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

开启线程为什么是执行start()方法而不直接执行run()方法

程序员文章站 2022-06-05 13:46:33
...

先看一段代码

public class Battles {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run(){
                pong();
            }
        };
        t.run();//重点在这,此处执行的是run()方法
        System.out.println(Thread.currentThread().getName());
        System.out.println("ping");
    }
    private static void pong() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("pong");
    }
}

大家可以试想一下输出是什么,在以往创建线程执行start()方法来看,新建的线程似乎比main线程要慢一点,“ping”和“pong”的输出可能并没有固定的顺序,而代码并不是执行的start()方法而是直接执行run()方法,到底有什么不一样呢,start()方法不依旧会执行run()方法吗?

先看结果:

main
pong
main
ping

发现了奇怪的一点,重写的run方法在输出线程名字的时候居然也是main,这两个线程都是用的同一个main线程,难道new Thread并没有帮我创建新线程吗?

带着这个疑问,深究一下start()方法和run()方法的区别----------直接上源码:

首先看run()方法

    @Override
    public void run() {
        if (target != null) {
            target.run();    //没有多余的操作,直接调用了重写的run()方法,并没有创建线程的操作
        }
    }

锁定start()方法

public synchronized void start() {       //首先表明了start方法是一个同步方法块
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)        //线程的状态,线程状态非正常就会抛出异常
            throw new IllegalThreadStateException();
            
        group.add(this);

        boolean started = false;
        try {
            start0();   //核心的地方就是调用了satrt0()方法
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }


	private native void start0();    //该方法并没有方法体,却有一个native的关键字修饰

先讲讲native关键字,native method就是Java调用非Java代码的接口,就是说native method 的方法实现由其他语言实现的,比如C语言等

/* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();  //在这个静态代码块中就使用了registerNatives将本地方法加载进来供给native method使用
    }

start0()方法会调用本地文件中的方法---------------还有诸多常见方法,如stop0,isAlive,yield,sleep等

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread}, // 可以看出调用了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},
};
#undef THD
#undef OBJ
#undef STE
#undef STR
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

JVM_StartThread方法会创建一个本地线程,最终会去调用run方法

总的来说:Java的里面创建线程之后必须要调用启动方法才能真正的创建一个线程,该方法会调用虚拟机启动一个本地线程,本地线程的创建会调用当前系统创建线程的方法进行创建,并且线程被执行的时候会回调跑方法进行业务逻辑的处理

总结:开启线程为什么是执行start方法而不是执行run方法:直接调用run方法该线程并不会创建出来,而是直接使用主线程去执行重写的run逻辑,只有调用start方法才会创建一个新的本地进程,通过本地进程去执行重写的run逻辑
相关标签: 深入Java java