从我在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,那么这种方法也可以避免创建一个额外的类。
这就是SOLID的s:单一责任。
线程体现了一段代码的异步执行的运行上下文(如执行上下文:堆栈帧、线程id等)。理想情况下,这段代码应该是相同的实现,无论是同步的还是异步的。
如果将它们捆绑在一个实现中,则会给结果对象两个不相关的更改原因:
应用程序中的线程处理(即查询和修改执行上下文)由代码段(可运行部分)实现的算法
如果您使用的语言支持部分类或多重继承,那么您可以在其自己的超级类中分离每个原因,但归结起来与组成两个对象相同,因为它们的特征集不重叠。这是为了理论。
在实践中,一般来说,一个方案不需要比必要的更复杂。如果您有一个线程在处理一个特定的任务,而不需要更改该任务,那么将任务划分为单独的类可能没有任何意义,并且代码仍然更简单。
在Java环境中,由于该工具已经存在,因此直接从独立的可运行类开始,并将其实例传递给线程(或执行器)实例可能更容易。一旦习惯了这种模式,它就不比简单的可运行线程情况更难使用(甚至读取)。