问题原型
文章题目说的有些文艺,所提的问题就是,运行如下代码,输出的结果是什么?(PS:不要怀疑问题,我保证如下代码没有任何语法错误)。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable-->run");
}
}) {
@Override
public void run() {
System.out.println("Thread-->run");
}
}.start();
答案揭晓
如上代码的运行后输出:Thread–>run,上图说真相:
为什么会这样?
遇到这种问题时,源码总能给我们最好的答案,所以,下面就从源码中找出为什么?
在看源码前,该思考要从源码的哪部分看起?如上的代码,总共涉及源码中的三部分,如下
- Thread类以Runnable对象作为参数的构造器;
- Thread类的Run方法的实现;
- Thread类的start方法的实现。
我从第三点切入,深入源码一探究竟。(注:我读的是 JDK1.8.0_60 的源码)。
Thread 的 start()方法的实现如下:
重点是调用了 start0()这个方法,继续追踪该方法:start0()方法的声明如下:
看到该方法是个native方法,它的实现应该在JVM中。该方法无法继续追踪,转而看看Thread的run()方法:Thread的run()方法实现如下:
结合方法的注释,并在源码中追查一下target变量的含义,不难发现这个target对象,就是用Thread的构造器创建对象时传进来的Runnable对象,而Thread的run方法的功能是,先判断target是否为null,如果不为null就调用target的run()方法。这时,谜团已经有了眉目了——用Runnable对象创建Thread对象时传递进来的Runnable对象的run()方法,是依靠Thread的run()方法的调用才得以执行。对于我们的问题,如上代码所示,我们在Thread子类中直接重写了run方法,Runnable对象的run方法没地方被调用,因此就没有执行的可能了。所有运行结果就如上所示。- 那么,问题来了:Thread的run()方法又是在哪里被调用的呢?在start()方法中并没有看到run()方法被调用啊?
继续从源码中找答案:看看start方法的注释,有这么一句,如下图中划横线的这句:
它的意思是:“jvm会调用这个线程的run方法”此时就清楚了,也就可以肯定,在上面的提到的start0()方法是在JVM中实现的,JVM通过start0()这个方法调用了Thread的run方法。
至此,所提问题得到解答。
拓展延伸
如果代码是如下的样子,结果又是什么呢?
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable-->run");
}
}) {
@Override
public void run() {
super.run(); // 相比原问题,多了这一句
System.out.println("Thread-->run");
}
}.start();
结果输出是:
Runnable–>run
Thread–>run
对于这个结果的解释,根据如上的原理剖析,读者应该心中有数了。