想要理解通过Runnable接口与继承Thread类两种创建线程方式的不同,首先要清楚任务与线程的区别。
你应该看到要执行的任务与驱动它的线程之间有一个差异,这个差异在Java类库中尤为明显,因为你对Thread类实际没有任何控制权(并且这种隔离在使用执行器是更加明显,因为执行器将替你处理线程的创建和管理)。你创建任务,并通过某种方式将一个任务附着到线程上,以使得这个线程可以驱动任务。
在Java中,Thread类自身不执行任何操作,它只是驱动赋予它的任务。线程不是任务,一旦某个类实现了Runnable接口,就意味着它要在run()
方法中执行某项任务,只有需要驱动这种任务时,才把它附着在一个线程上。
因此很容易认识到他们的区别:
- 实现Runnable接口后,必须要重写
run()
方法;继承Thread后,可以不重写run()
方法,(根据接口和类的定义也很好理解)。 - 实现Runnable接口的类,它
run()
中的任务需要通过Thread驱动;继承Thread类的子类,可通过start()
方法直接驱动run()
中的任务(Thread默认不执行任何操作并返回)。
public class RunExample implements Runnable{
private static int countDown = 5;
//必须重写run()方法
@Override
public void run() {
// TODO Auto-generated method stub
while(countDown>0) {
System.out.println("#"+"("+(countDown--)+")");
}
System.out.println("Liftoff!");
}
//驱动run()方法中的任务时,需要附着在一个Thread对象上
public static void main(String[] args) {
Thread t = new Thread(new RunExample()) ;
t.start();
}
}
public class ThreadExample extends Thread{
private static int countDown = 5;
//可以不重写run()方法,那样的话可默认不执行任何任务并返回
public void run() {
while(countDown>0) {
System.out.println("#"+"("+(countDown--)+")");
}
System.out.println("Liftoff!");
}
//驱动时直接运用start()方法
public static void main(String[] args) {
new ThreadExample().start();
}
}
我们还可以用一种非常巧妙的方法,对run()
方法实现“自我驱动”,即在实现了Runnable接口的类中内置一个Thread线程,把自身附着在Thread上,并在这个类的构造方法中用这个内置的Thread启动任务,这样的话只要这个类的对象已创建,就自行执行任务。
private Thread t = new Thread(this);
public SelfRunExample() { t.start(); }
new SelfRunExample();
public class SelfRunExample implements Runnable{
private static int countDown = 5;
private Thread t = new Thread(this);
public SelfRunExample() {
t.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(countDown>0) {
System.out.println("#"+"("+(countDown--)+")");
}
System.out.println("Liftoff!");
}
public static void main(String[] args) {
new SelfRunExample();
}
}
这与从Thread继承并没有什么特别差异,但是创建对象时不用写.start()
了,而且这样可以使它可以继承另一个不同的类,而从Thread继承不行。(因为Java不支持多继承)
这样也有缺点,因为在构造器中执行线程可能会变得很有问题,因为另一个任务可能会在构造器结束之前执行,这意味着该任务能够访问处于不稳定状态的对象。这也是优选Executor而不是显式地创建Thread对象的另一个原因。
有时通过使用内部类来将线程代码隐藏在类中将会很有用。
class InnerThread1 {
private int countDown = 5;
private Inner inner;
private class Inner extends Thread{
Inner(String name){
super(name);
start();
}
public void run() {
try {
while(true) {
System.out.print(this);
if(--countDown==0)
return;
sleep(10);
}
}catch(InterruptedException e) {
System.out.print("interrupted");
}
}
public String toString() {
return getName()+": "+countDown;
}
}
public InnerThread1(String name) {
inner = new Inner(name);
}
}