为什么 Java 同时提供 Thread Class 和 Runnable 接口来创建线程?

Why Java provides both the Thread Class and Runnable Interface for creating Threads?

我知道具体线程 class 和 Java 中的可运行接口之间的区别。使 Thread class 可重写以供使用的必要条件是什么?是否可以通过实现 Runnable 接口来创建所有线程?需要 Thread class 的用例是什么?为什么我们有两种方法来解决同一个问题?

编辑:我知道 Thread class 是 Runnable 实现的容器,我想知道是否有任何 Runnable 实现无法解决的用例

Java 对于 classes 没有多重继承概念,但是对于接口它是。

假设您有一个 class,它已经扩展(继承)了一个 class

class A extends Parent{

}

以上 class 无法扩展另一个 class 喜欢

class A extends Parent extends Thread{ // Incorrect
    
    }

但它可以有类似

的内容
class A extends Parent implements Runnable{
    
    }

现在对于上述情况,如果你想利用 multi threading,你可以很容易地做到这一点。

多看here

Why Java provides both the Thread Class and Runnable Interface for creating Threads?

简短的回答是“历史”。

在 ~1995 年,Java 1.0 定义了 Thread 以便您可以扩展 class,或将 Runnable 传递给构造函数。到大约 Java 1.1 时,设计师很清楚扩展 Thread 是一个糟糕的选择。但已经来不及修复了。将 Thread 更改为 final class 不再是一个选项,因为它会破坏向后兼容性。

前滚到 2022 年,同样的推理仍然适用。仍然有许多重要的遗留代码,程序员在其中扩展 Thread 以使用包含应用程序逻辑的方法覆盖 run()。代码运行良好……所以强迫许多 Java 用户更新到“更好”的方式将是一个糟糕的商业决策。


他们当初为什么做出这个决定?谁知道!我的猜测是,早在 1995 年,他们就没有想到线程池、执行程序等如果扩展 Thread 就会出现问题。 (还有一些其他的事情......正如多年前被弃用的不安全或无法实现的 Thread 方法所说明的那样。)

一个可能的因素是 Java 1.0 没有匿名内部 classes。因此,要使用构造函数参数方法,程序员需要声明一个扩展 Runnable 的命名 class。扩展 Thread 是一个“有吸引力的”选择。 IIRC,Java 1.1 中解决了该语言缺陷。在 Java 8+ 中,我们还可以使用 lambda 表达式来定义线程可运行对象。

记住:在设计 Java 时,线程是 一个新事物 。 Java 是首批正确 支持它们的编程语言之一。 (保证不犯错误的唯一方法就是什么都不做。这往往是一个 更大 的错误。)


Can all threads be created by implementing the Runnable interface ?

没有。创建线程所涉及的所有逻辑实际上都在 Thread class ...及其在 JVM 中的本机代码实现中。在 Java 代码中自己执行此操作是不切实际的。

因为如果我们扩展 Thread class 我们就不能扩展任何其他的 class 因此 java 提供可运行接口 如果我们实现可运行接口则强烈推荐使用可运行接口然后我们也可以扩展任何其他接口

I know the differences between the concrete Thread class and the Runnable Interface in Java. ... Can all threads be created by implementing the Runnable interface? What will be the use case where the Thread class is a necessity?

您不能仅通过实现Runnable 接口来创建线程。即使还有一个单独的 Runnable,也必须始终有一个 ThreadThread class 对于 Java 程序中的 multi-threading 是必不可少的。*

但是请注意!这并不意味着您自己的代码必须执行 new Thread(...)new MyFoobarThread().

Why we have two ways to solve the same problem ?

我们有 许多 方法来解决问题:您的代码可以请求 ThreadFactory 创建新实例。您的代码可以创建一个 ThreadPoolExecutor 实例,并让执行程序管理线程。您的代码可以使用基于线程的并行流。您的代码可以调用使用线程的第三方库。但是,无论它如何发生,程序中的每个线程总会有一个 Thread 实例。

[* native 方法可以创建一个没有相应 Thread 实例的线程,但在那种情况下,线程将无法调用 Java 方法, Java 代码将完全不知道该线程的存在。]


What is the need for making the Thread class overridable...?

这不是需要的问题。 Thread 是一个 class。 每个 class 不是 final 的都可以扩展。使 Thread 最终化的目的是什么?如果扩展它解决了一些问题,阻止程序员扩展它的原因是什么? (我知道可以通过扩展 Threadwrapping 它的某些方法来解决问题,但我不会在这里深入讨论。)

编程语言的目的是帮助程序员做他们需要做的事情,而不是在他们的道路上设置障碍。


Runnable最初是为了与Threadclass一起使用而创建的—— a bit about the history of that, and 有点原因——但是Runnable,它变成了out,不仅对线程有用。

Runnable 是一种 行为契约。 它描述了“需要发生的事情”,“需要完成的工作”。它 而不是 描述工作的执行者或工作时间。这项工作现在可以由 Thread 完成,可以尽快由线程 pool 完成,可以由 timer 在某个指定的时间,它可以在其他任务完成后由第三方库完成,等等。

一个 Thread 有一个 一个 Runnable 就像咖啡壶 有一个 把手一样。有把手大大提高了咖啡壶的实用性,但其他东西也有把手,原因与煮咖啡无关。