这有点棘手; 对于每种方法,答案都不同。让我们逐个查看您提到的方法:
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
并经常检查它。
这就是“中断”系统的作用。其想法是:
要中断任何线程,请使用thatThread.interrupt();
提高其中断标志。
所有执行可中断操作的方法都应该检查此标志。具体过程是:如果标志被提高,则[A]清除它,并[B]处理中断,执行程序员期望在中断发生时要执行的操作(停止运行、重新检查某些条件、重新读取某些配置文件等等-这是编程,你想让它意味着什么就什么)。如果您可以处理中止某个操作的概念,但不能处理它,则清除该标记并抛出InterruptedException,以便调用者可以处理它。
因此,任何知道“我已被中断!”的代码都应该同时检查该标志(特别是如果该代码有一个事件循环,大多数基于线程的代码都有),并从任何指定抛出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类以查找描述所需内容的方法。然后检查该方法是否为静态方法。如果它是静态的,则不能对任何其他线程执行操作(例如清除中断标志或睡眠)。如果它是实例,则可以对其他线程执行此操作(例如更改其优先级级别)。