Thread类已经有了currentThread()方法,为什么还需要静态方法?

4

Thread类有许多可通过类名调用的静态方法,其中一些为: enter image description here

但是,我们提供了一个方法currentThread()来返回当前正在执行的线程对象。一些示例: enter image description here

不幸的是,这在我的脑海中造成了困惑。当我想到我需要的方法时,我不知道是否会将其找到为静态实例。那么为什么他们要使用这两种不同的方法呢?

我的意思是,他们不能都归于同一种“调用”中吗?例如,为什么sleep()静态而不是带着Thread.currentThread().sleep()的实例方法?另一个奇怪的例子是interrupted()isInterrupted()以不同的方式定义。它们完全做相同的事情,只是interrupted()额外清除中断标志。是否有任何逻辑答案,让我不必费力找到每个方法?


不,中断设置标志并且被中断的程序获取(并清除)它。 - Brian McCutchon
哦,对,我是指interrupted()。我会更正的。 - Stefan
那并不能完全纠正它。它们不做相同的事情。它们实际上是相反的。关键是你可以中断(interrupt())任何线程,但只能告诉你自己的线程是否被中断(interrupted())。 - Brian McCutchon
2个回答

8

这有点棘手; 对于每种方法,答案都不同。让我们逐个查看您提到的方法:

Thread.sleep

假设我调用了:someOtherThread.sleep(1000L);。这意味着什么?肯定应该是让那个线程休眠,而不是我的线程。但 Java 并没有提供这样的功能:您可以使您自己的线程休眠,但您不能随意告诉其他线程像做哑剧一样冻结,中断执行某些任意命令。例如,如果该线程当前正在阻塞等待操作系统从文件读取中传递一些字节,那么它肯定不能睡觉,还有许多其他情况下线程不能这样做。

因此,Java 不提供此功能 - 您无法控制其他线程的休眠。只能控制自己的。在 API 设计中,有两种不同的方式可以使此问题至少有些清晰:

第一种是将 sleep 设为实例方法(因此,您必须编写例如Thread.currentThread().sleep(1000L);),并规范该方法保证,始终立即抛出一个 IllegalStateException,如果你在其他线程上调用它。这意味着在运行时仅能捕捉到编译/编写时可检测的错误条件(这很糟糕,尽早捕获问题显然比晚些捕获更好),会使需要休眠的代码变得不必要的长,并且可以调用线程实例的 sleep 方法的存在肯定暗示您可以休眠其他线程。这只是一个糟糕的 API 设计。

第二种方法是使 sleep 是静态的。

以这样的方式思考:java.lang.Thread 是两个主要不相关的方法集合的容器:一组您可以在线程上使用的方法(那将是实例方法)。另一组是一些线程和流相关的基元,例如 "sleep"、"yield" 和中断交互。它们只是被塞进同一个类中而已。

interrupt

这可能是最棘手的。与休眠不同,您实际上可以询问另一个线程的中断标志状态。

之所以有两个方法,是因为中断系统的 API 设计或多或少地是有意的。

中断系统的设计如下:

如果您想让某个线程出于某种未指定的原因停止当前操作(例如,您希望它重新检查某些条件,或者只是停止运行,或者您能想到的任何其他操作),则需要一种机制来表示这一点。特别地,您希望这样的机制确保任何可中断的阻塞操作(例如Thread.sleep(100000L))都会被中断。换句话说,您不能简单地说:无论如何,由代码本身处理,只需创建一个 AtomicBoolean 并经常检查它。

这就是“中断”系统的作用。其想法是:

  1. 要中断任何线程,请使用thatThread.interrupt();提高其中断标志。

  2. 所有执行可中断操作的方法都应该检查此标志。具体过程是:如果标志被提高,则[A]清除它,并[B]处理中断,执行程序员期望在中断发生时要执行的操作(停止运行、重新检查某些条件、重新读取某些配置文件等等-这是编程,你想让它意味着什么就什么)。如果您可以处理中止某个操作的概念,但不能处理它,则清除该标记并抛出InterruptedException,以便调用者可以处理它。

  3. 因此,任何知道“我已被中断!”的代码都应该同时检查该标志(特别是如果该代码有一个事件循环,大多数基于线程的代码都有),并从任何指定抛出InterruptedException的方法中捕获InterruptedException,并以完全相同的方式对捕获该异常或有 Thread.interrupted()返回true响应。

如果您处理了中断标志已启用的事实,但没有将其降低,则情况会变得非常糟糕。例如,如果你中止了CPU密集型比特币挖掘或其他操作,只是返回给调用者,同时保持该标志不变,则下一次调用Thread.sleep时,线程就会注意到该标志已启用,并立即退出,根本不休眠(具体来说,是通过抛出InterruptedException退出)。这不是我们想要的。因此,如果您响应中断,请降低该标志。

现在让我们回到API设计。有两种策略:

假设的设计A

while (!Thread.currentThread().isInterrupted()) {
    mineAnotherBitCoin();
}
Thread.currentThread().clearInterruptFlag();

设计B

while (!Thread.checkAndClearInterruptFlag()) {
   mineAnotherBitCoin();
}

请注意,设计B在概念上要短得多,没有在检查标志和清除标志之间有“间隙”,因此基本上更少出错。此外,由于某些原因,决定将引发中断标志作为可以对其他线程执行的操作(毕竟,中断自己没有意义),但清除标志只能对自己的线程执行。
Java实际上拥有B,只是方法的名称有些奇怪,不是checkAndClearInterruptFlag(),而是interrupted()。如果您想解释一下为什么Java中的某些方法名称有点可疑,那是因为Java不喜欢破坏向后兼容性。
因此,尽管它们听起来非常相似,isInterrupted()和interrupted()做的事情完全不同。
isInterrupted()用于检查某个线程是否已被中断,其对此中断的响应仍处于未决状态(尚未处理)。
interrupted()是您放置在while循环条件中的东西,这些条件定义了您的线程实现(即“事件循环”的核心体)。
*)并不是所有的java线程制作示例都正确,因为它们通常会忽略中断,或者使用自定义的类似中断的“running”概念,例如while(true)或while(!running){}等。
那我该去哪里找呢?
很简单:如果是概念上不属于任何特定线程的事物(例如“有多少个线程现在处于活动状态”),或者它是一个实用程序概念(例如“休眠”),或者从VM设计原则来看,只能对自己的线程而不是其他东西进行,则它是Thread中的静态方法。
如果是属于特定线程的事物且VM会让您将其应用于其他线程(例如中断它、请求其名称、ID或优先级、获取堆栈转储、冻结此线程直到其他线程完成或设置其优先级),则它是实例方法。
在许多方面,您可以反向使用这种逻辑:如果要处理一些与线程相关的业务,请检查Thread类以查找描述所需内容的方法。然后检查该方法是否为静态方法。如果它是静态的,则不能对任何其他线程执行操作(例如清除中断标志或睡眠)。如果它是实例,则可以对其他线程执行此操作(例如更改其优先级级别)。

2

因为你不能使另一个不属于当前线程的线程休眠。即使你调用 Thread.currentThread().sleep(),你也是在调用静态方法 'sleep'。如果你在不同的 Thread 对象上调用 sleep 方法,它仍然会使当前线程休眠。

如果你想使不同的线程休眠,你应该设置一个标志,让其他线程读取,这会导致它休眠。


基本上,Thread静态方法通常用于只能在当前线程上执行的操作。 - Brian McCutchon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接