从我在Java中使用线程的时间来看,我发现了两种编写线程的方法:
使用可运行的机具:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
或者,使用扩展线程:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
这两个代码块有什么显著的区别吗?
随着Java8的发布,现在有了第三个选项。
Runnable是一个函数接口,这意味着可以使用lambda表达式或方法引用创建它的实例。
您的示例可以替换为:
new Thread(() -> { /* Code here */ }).start()
或者如果要使用ExecutorService和方法引用:
executor.execute(runner::run)
这些不仅比示例要短得多,而且还具有其他答案中所述的使用Runnable over Thread的许多优点,例如单一责任和使用组合,因为您没有专门处理线程的行为。如果您只需要一个Runnable,那么这种方法也可以避免创建一个额外的类。
实现Runnable和扩展Thread之间的一个区别是,通过扩展Thread,每个线程都有一个与其关联的唯一对象,而实现Runnable,许多线程可以共享同一个对象实例。
实现Runnable的类不是线程,只是一个类。对于要由线程执行的Runnable,需要创建一个Thread实例,并将Runnable实例作为目标传入。
在大多数情况下,如果只打算重写run()方法而不打算重写其他Thread方法,则应使用Runnable接口。这一点很重要,因为除非程序员打算修改或增强类的基本行为,否则类不应该被子类化。
当需要扩展超类时,实现Runnable接口比使用Thread类更合适。因为我们可以在实现Runnable接口时扩展另一个类以生成线程。但是如果我们只是扩展Thread类,我们就不能从任何其他类继承。
实际上,将Runnable和Thread相互比较是不明智的。
这两者在多线程中具有相关性和关系,就像机动车的车轮和发动机关系一样。
我想说,多线程只有一种方法,有两个步骤。让我表明我的观点。
可运行:当实现接口Runnable时,这意味着您正在创建可在不同线程中运行的东西。现在创建可以在线程内运行的东西(可以在线程中运行)并不意味着创建线程。因此,MyRunnable类只是一个带有void run方法的普通类。它的对象将是一些普通对象,只有一个在调用时正常执行的方法运行。(除非我们在线程中传递对象)。
线程:class线程,我可以说是一个非常特殊的类,它能够启动一个新的线程,这实际上通过它的start()方法实现了多线程。
为什么不明智地进行比较?因为我们需要这两个线程来实现多线程。
对于多线程,我们需要两件事:
可以在线程内运行的东西(可运行)。可以启动新线程的东西。
因此,从技术上和理论上来说,这两个都是启动线程所必需的,一个线程将运行,另一个线程会使线程运行(就像机动车的车轮和发动机)。
这就是为什么不能使用MyRunnable启动线程,需要将其传递给thread的实例。
但是,只有使用类thread才能创建和运行线程,因为类thread实现了Runnable,所以我们都知道thread内部也是一个Runnable。
最后,Thread和Runnable是多线程的补充,而不是竞争对手或替代者。
在这里加上我的两分钱-始终尽可能使用机具Runnable。以下是关于为什么不应该使用扩展线程
理想情况下,您不应该扩展Thread类;线程类应为最终类。至少它的方法像thread.getId()。有关扩展线程的错误,请参阅本讨论。那些喜欢解谜的人可以看到扩展线程的另一个副作用。以下代码将在无人通知时打印无法访问的代码。
请参见http://pastebin.com/BjKNNs2G.
public class WaitPuzzle {
public static void main(String[] args) throws InterruptedException {
DoNothing doNothing = new DoNothing();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
Thread.sleep(100);
doNothing.start();
while(true) {
Thread.sleep(10);
}
}
static class WaitForever extends Thread {
private DoNothing doNothing;
public WaitForever(DoNothing doNothing) {
this.doNothing = doNothing;
}
@Override
public void run() {
synchronized (doNothing) {
try {
doNothing.wait(); // will wait forever here as nobody notifies here
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Unreachable Code");
}
}
}
static class DoNothing extends Thread {
@Override
public void run() {
System.out.println("Do Nothing ");
}
}
}