Java/线程的 start 和 run 方法的区别

Java/线程的 start 和 run 方法的区别

  • 直接调用线程的 run 方法,并不会启动一个新的线程,而是在原来的线程里面执行 run() 方法里面的语句,与普通的方法调用没有差别
  • 调用线程的 start 方法,是启动一个新的线程去执行 run() 方法里面的代码 如下示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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() 方法,输出是:
    1
    2
    3
    Current Thread is: main
    Fight
    Current Thread is: main
    把其中的t.run();改成t.start();后,输出如下:
    1
    2
    3
    Current Thread is: main
    Fight
    Current Thread is: Thread-0
    证明了 start() 方法会开启一个新的线程,而 run() 方法不会开启新线程 我们可以进入到 start 方法的源码中查看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
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
1
2
3
4
5
6
7
8
9
10
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 的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12

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 接口

评论