面试必问,面试必问,面试必问!别问我为什么知道~~
总结
- run()只是Thread类的一个普通方法,调用run()并不会新建一个子线程,仍在主线程执行任务。
- 调用start()会新建一个子线程并执行run()的内容。调用start()会有两个线程,一个是当前正在调用start()的主线程;另一个是JVM创建的子线程,用来执行run()的内容。
实践
实践出真知~~首先写下测试代码:
public class ThreadTest {
static void printThreadName(){
System.out.println("printThreadName: "+Thread.currentThread().getName());
}
public static void main(String[] args) {
// 也可用lambda表达式替换,更简洁:new Thread(() -> printThreadName());
Thread thread = new Thread(){
@Override
public void run() {
printThreadName();
}
};
System.out.println("main thread name: "+Thread.currentThread().getName());
thread.run();
}
}
打印结果:
main thread name: main
printThreadName: main
说明使用run()方法并未另起一个线程执行,而是使用主线程执行。run()方法只是Thread的一个普通方法。
我们将上面代码中的run()方法换成start()试试看。
public class ThreadTest {
static void printThreadName(){
System.out.println("printThreadName: "+Thread.currentThread().getName());
}
public static void main(String[] args) {
// 也可用lambda表达式替换,更简洁:new Thread(() -> printThreadName());
Thread thread = new Thread(){
@Override
public void run() {
printThreadName();
}
};
System.out.println("main thread name: "+Thread.currentThread().getName());
thread.start();
}
}
打印结果:
main thread name: main
printThreadName: Thread-0
发现调用start()方法会新建一个子线程执行run()方法的内容。为什么呢?下面深入源码:
/**
* JVM会调用该线程的run()方法去执行。
*
* 执行该方法会有两个线程:一个是当前线程,用来执行start()方法;另一个线程去执行它的run()方法。
*
* start()只能被调用一次,否则会提示IllegalThreadStateException
*
*/
public synchronized void start() {
/**
* 0 表示新建状态
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
//忽略不重要的代码
.....
try {
start0();
} finally {
....
}
//本地方法:说明不是用java写的
private native void start0();
查看源码,发现start()最终调用start0(),start0()是一个native方法,要看这块代码做了什么,我们可以到这个网站去查询(jdk8u-thread.c)。
发现该方法调用了JVM去启动一个新线程。
{"start0", "()V", (void *)&JVM_StartThread},
通过同个网站(jdk-jvm)查看对应的JVM_StartThread源码。
// 新建了一个线程
native_thread = new JavaThread(&thread_entry, sz);
// 调用了该线程的run()方法。
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);
}
就此结束啦~
看懂的朋友可以再做下练习题,最后打印什么?“pingpong"还是”pongping",还是都有可能呢?
public class ThreadTest {
static void printThreadName(){
System.out.println("ping");
}
public static void main(String[] args) {
// 用lambda表达式替换,更简洁:
new Thread(() -> printThreadName()).run();
System.out.println("pong");
}
}