线程的 start 和 run 方法的区别

张贤 2020年03月11日 129次浏览
  • 直接调用线程的 run 方法,并不会启动一个新的线程,而是在原来的线程里面执行 run() 方法里面的语句,与普通的方法调用没有差别
  • 调用线程的 start 方法,是启动一个新的线程去执行 run() 方法里面的代码
    如下示例:
public class ThreadTest {
    private static void attack(){
        System.out.println("Fight");
        // 打印当前线程名字
        System.out.println("Current Thread is: "+ Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                //在子线程里面调用 attack 方法,打印线程名字
                attack();
            }
        };
        //打印主线程名字
        System.out.println("Current Thread is: "+ Thread.currentThread().getName());
        //调用线程的 run() 方法
        t.run();
    }
}

上述代码中,调用线程的 run() 方法,输出是:

Current Thread is: main
Fight
Current Thread is: main

把其中的t.run();改成t.start();后,输出如下:

Current Thread is: main
Fight
Current Thread is: Thread-0

证明了 start() 方法会开启一个新的线程,而 run() 方法不会开启新线程
我们可以进入到 start 方法的源码中查看:

    public synchronized void start() {
        ...
        ...
        ...
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
        ...
        ...
        ...
        }
    }

可以看到在 start() 方法里调用了 start0() 方法来启动一个新线程的,而 start0() 方法是一个 native 方法,可以通过下面链接查看 openJDK 对应的代码http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/6f7370a85071/src/share/native/java/lang/Thread.c

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    ...
    ...
    ...
};

可以看到 start0() 方法实际对应的是 JVM_StartThread 方法,对应的代码在http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/b4fd7e078c54/src/share/vm/prims/jvm.cpp

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){
    ...
    ...
    ...
    //创建一个新线程
    native_thread = new JavaThread(&thread_entry, sz);
    ...
    ...
    ...
}

可以看到在 JVM_StartThread 方法中最终是通过new JavaThread(&thread_entry, sz)创建一个新线程的。其中 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()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}

可以看到在 thread_entry 中传入了 run_method_name,也就是 run 方法的名字,所以新创建的线程运行的就是 run 方法。调用关系图如下:


#### Thread 和 Runnable 是什么关系 - Thread 是实现了 Runnable 接口的类,使得 run 方法支持多线程。我们可以通过重写 Thread 的 run 方法实现多线程,也可以将 Runnable 的对象传递给 Thread 的构造方法实现多线程。 - 由于类的单一继承原则,为了提升代码的可扩展性,推荐多使用 Runnable 接口