很多时候我们都知道在Java中创建线程类的方式有最常见的两种:
- 实现Runnable接口
- 继承Thread类
其实我也是这样认为的直到有一天无意中看到了Runnable和Thread源码,才真正明白了其中的道理。
Runnable源码:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
源码中对于run()方法的解释是:当使用实现Runnable接口的对象创建线程时,启动线程会导致在单独执行的线程中调用对象的 run 方法。
Thread源码:
public class Thread implements Runnable {
...
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
...
}
其实看到这个源码就已经明白了,Runnable接口里面就真是只是一个接口,里面有一个抽象方法run()等着被重写,再看看那Thread类居然是实现了Runnable接口,覆盖了run方法,说白了Thread也是实现了Runnable接口。
下面看看用户自定义线程类:
1.继承Thread类
class TestThread1 extends Thread {
public void run(){
System.out.println(Thread.currentThread().getName()+"\t <TestThread1> is ok");
}
}
2.实现Runnable接口
class TestThread2 implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName()+"\t <TestThread2> is ok");
}
}
启动方式:
1.继承Thread类
用户自定义线程类可以作为Thread类的构造参数,也可以直接new 然后调用start()启动线程,为何可以这样顺畅?因为Thread类的构造方法:
public Thread(Runnable target) {}
这个构造方法中的参数类型是一个Runnable接口类型的,而TestThread1是Thead的子类,自然也是相当与实现了Runnalbe接口的,因此这里可以作为Thread的构造参数的。
因为TestThread1是Thread的子类,因此可以直接创建TestThread1的子类,然后调用start()启动线程。
Thread t = new Thread(new TestThread1());
t.start();
TestThread1 t3 = new TestThread1();
t3.start();
运行结果:
Thread-1 <TestThread1> is ok
Thread-2 <TestThread1> is ok
2.实现Runnable接口
Thread t2 = new Thread(new TestThread2() );
t2.start();
这种方式创建的线程类,只能是实例化之后作为Thread类的构造参数,然后调用Thread类的stat()方式启动线程。
两种方式创建线程其实本质是相同的, 但是从实际的应用角度来讲,只要是创建线程类绝大多数是以实现Runnable接口为主的方式,好处有2:
1、避免继承的局限,一个类可以继承多个接口。
2、适合于资源的共享。