Runnable和Thread源码分析
问题的引入:
Runnable是一个接口,它只有一个run()抽象方法,run()抽象方法需要被实现后才能使用。Thread类继承了Runnable接口,并且Thread中实现了run()方法。最后,通过Thread类的start()方法就可以启动线程了。这是我们平时在应用中使用线程的常用方法。如果多加思考你会发现以下几个问题:
1、为什么调用start()方法就可以启动线程,而不是调用run()。start()方法和决定线程运行内容的run()方法又有什么联系呢?
2、在Runnable层面实现run()方法和Thread层面实现run()方法的区别。
3、怎么用Runnable实现run方法,并在Thread中执行Runnable实例化后重写的run方法。
不着急下结论,我们通过分析源码来理清两者的关系。
Runnable源码:
打开如下Runnable的源码,我们可以发现,它只含有一个抽象的run()方法。因为它是接口,所以需要被继承后重写run()方法才能使用。我们可以简单思考一下,如果需要启动一个新的线程,这意味着需要分配给它CPU资源来执行这个线程,而CPU是不归JVM(Java虚拟机)直接管辖的,自然需要通过JVM通过外部的接口来实现和操作系统的对话,调整CPU资源的分配,所以,线程的创建一定是一个native方法(实现Java调用底层的C、C++代码)。接口中的run()方法中的内容,仅仅代表线程运行的内容。一个结论出来了,Thread类中必然有native方法,Runnable无法脱离Thread类来新建线程。如果我们在new一个线程对象后直接调用run方法,也只是让当前线程去执行run()方法中的语句罢了,并没有实现多线程。
public interface Runnable {
public abstract void run();
}
Thread源码:
接下来我们看看Thread类的源码。
从下面这句我们可以看出Thread继承了Runnbale接口。
public class Thread implements Runnable {
下面是Thread类的run()方法。里面的target对象是什么?为什么target能调用run()方法?这个run()方法是谁的run()方法?
@Override
public void run() {
if (target != null) {
target.run();
}
}
为了解决问题,我们把关注点放到Thread类的这几行代码上。在Thread类的一个构造方法中Runnable接口的实例化对象target被传入其中,通过几个方法的调用,并通过this.target = target;语句把Runnable的实例化对象保存在了Thread类中,成为了Thread类的一个成员变量,那么它调用的run()方法自然是在Runnable实例化中重写的run()方法。
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
<