1.实现线程的方式
Oracle官方文档阐述了两种实现线程的方式,一种是实现Runnable接口并重写run方法,另一种是继承Thread类并重写run方法,两种实现方式的代码分别如下所示。

/**
* 描述:使用Runnable接口的方式实现线程。
*/
public class RunnableStyle implements Runnable {
@Override
public void run() {
System.out.println("使用Runnable接口的方式实现线程");
}

public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
}
/**
* 描述:使用继承Thread类的方式实现线程。
*/
public class ThreadStyle extends Thread{
@Override
public void run() {
System.out.println("使用继承Thread类的方式实现线程");
}

public static void main(String[] args) {
ThreadStyle thread = new ThreadStyle();
thread.start();
}
}

2.两种实现方式的区别
官方建议使用Runnable方式实现线程,原因主要有以下三点。

  • Runnable可避免Java单继承的缺陷。如果一个类本身已经继承一个父类,那么该类将无法通过继承Thread类的方式实现线程。
  • Runnable可避免子类继承Thread类中除run和start以外的方法。实现线程任务的run方法,应该要将其从该类中解耦出来。
  • Runnable可以有共享数据。使用同一个Runnable构造的不同线程之间是可以共享数据的,而Thread类不能共享数据。

3.实现细节
从上述两种实现方式的代码来看,本质上,都是通过Thread类的构造方法来创建线程的,只是一个传递了Runnable实例,一个没有。如果继承Thread类,那么run方法会被完全覆盖,执行子类的run方法,里面的判断逻辑将不会执行。而实现Runnable接口时,传入Runnable实例,run方法里的判断逻辑将会被执行,调用的是实例的run方法。具体源代码如下所示。

public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
private Runnable target;

private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
init(g, target, name, stackSize, null, true);
}

public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}

@Override
public void run() {
if (target != null) {
target.run();
}
}
}

4.思考题
分析以下代码执行结果并思考原因。

/**
* 描述:同时使用Runnable和Thread两种实现线程的方式。
*/
public class BothRunnableAndThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用Runnable接口的方式实现线程");
}
}){
@Override
public void run() {
System.out.println("使用继承Thread类的方式实现线程");
}
}.start();
}
}

代码运行结果如下所示,由此可知,当同时使用两种线程实现方式时,执行的是Thread类的run方法,原因是当Thread类的run方法被覆盖时, target.run()便不会被执行,即Runnable的run方法不会被执行。

使用继承Thread类的方式实现线程