这很棘手;每种方法的答案都不同。让我们来看看你命名的那些:
线程.sleep
想象一下我打电话给:someOtherThread.sleep(1000L);。这意味着什么?当然,这应该意味着:睡眠其他线程,而不是我的线程。除了这不是java提供的东西:你可以让你自己的线程休眠,但你不能随意告诉其他线程冻结,就像他们在做一个哑剧一样,中间执行一些任意命令。例如,如果该线程当前被阻塞,例如,等待操作系统从文件读取中传递一些字节,那绝对不能只是睡着了,还有很多其他情况下线程无法做到这一点。
因此,java 不提供此功能 - 你不能休眠其他线程。只有你自己的。在 API 设计中至少有两种不同的方法可以使这一点更加清晰:
第一个是让 sleep 成为一个实例方法(因此,您必须编写例如Thread.currentThread().sleep(1000L);),并指定它将保证的方法,如果您调用它,它将始终立即抛出IllegalStateException除了你自己的任何线程。这意味着编译/写入时可检测到的错误条件只会在运行时被捕获(这很糟糕;早发现问题显然比晚发现要好),它会使你不得不编写的代码不必要地更长地休眠,并且您可以在线程实例上调用的 sleep 方法的存在确保建议您可以休眠其他线程。这只是糟糕的 API 设计。
二是让睡眠静态化。
这样想:java.lang.Thread 是两个大部分不相关的方法批次的容器:一个是您可以在线程上使用的一组方法(那些是实例方法)。另一个是一堆线程和流相关的原语,比如'sleep'、'yield'和中断交互。他们只是碰巧被挤进了同一个班级。
中断
这可能是最棘手的。与睡眠不同,您实际上可以询问另一个线程的中断标志状态。
之所以有两种方法,是因为中断系统的 API 设计或多或少是有意的。
中断系统设计如下:
如果您希望某个线程由于某种未指定的原因而停止它正在执行的操作(例如,您希望它重新检查某些条件,或者只是停止运行,或者您能想到的任何其他事情),那么您需要一种机制表示这一点。特别是,您需要这样一种机制来确保任何可中断的阻塞操作,例如Thread.sleep(100000L) 被中断。换句话说,你不能只说:无论如何,这取决于代码本身,只是,嗯,创建一个AtomicBoolean 并检查它。
这就是“中断”系统的用武之地。想法是:
-
要中断任何线程,请使用thatThread.interrupt(); 提高其中断标志
-
所有做可中断事情的方法都应该检查这个标志。过程是:如果它被提出,然后[A]清除它,然后[B]处理中断,做任何程序员打算在中断时发生的事情(停止运行,或者重新检查一些条件,重新读取一些配置文件,谁知道——它是编程,不管你想表达什么意思)。如果您可以处理中止某些操作的概念,但您无法处理它,则改为清除该标志并抛出 InterruptedException,以便调用者可以处理它。
-
因此,任何知道“我被打断了!”的代码意味着应该同时检查标志(特别是如果该代码具有事件循环,大多数基于线程的代码确实具有),并从指定抛出它的任何方法中捕获 InterruptedException,并以完全相同的方式对捕获该异常或让Thread.interrupted() 返回 true。
如果您处理了中断标志已打开的事实,但您不降低它,事情就会发生各种错误。例如,如果你中止你的 CPU-bound 比特币挖掘或诸如此类的,只是返回给你的调用者,同时保持标志,那么下次调用者调用 Thread.sleep 时,thread.sleep 会注意到标志已经启动并立即退出,根本不睡觉(具体来说,通过抛出 InterruptedException 退出)。那不是故意的。因此,为什么在响应中断时降低该标志很重要。
那么,让我们回到 API 设计。有两种策略:
假设设计A
while (!Thread.currentThread().isInterrupted()) {
mineAnotherBitCoin();
}
Thread.currentThread().clearInterruptFlag();
设计 B
while (!Thread.checkAndClearInterruptFlag()) {
mineAnotherBitCoin();
}
请注意,设计 B 在概念上要短得多,在检查标志和清除标志之间没有“间隙”,因此从根本上更不容易出错。此外,出于某种原因,已经决定提高中断标志是您可以对其他线程执行的操作(毕竟中断自己没有意义),但是清除一个中断标志是您只能对自己执行的操作线程。
B 是 java 实际拥有的,只是该方法有点奇怪地命名为 interrupted(),而不是 checkAndClearInterruptFlag()。如果您想解释为什么 java 中的某些方法的命名有些可疑,那是因为 java 不喜欢破坏向后兼容性。
从根本上说,虽然它们听起来很相似,但isInterrupted() 和interrupted() 做了两件非常不同的事情。
isInterrupted() 是检查某个线程是否已经被中断,并且它对这个中断的响应仍然是未决的(还没有处理它)。
interrupted() 是您在定义线程实现的核心主体(您的“事件循环”)的 while 循环中放入条件的内容。
*) 绝大多数关于如何在 java 中创建线程的示例都是错误的,因为它们没有正确执行此操作,这无济于事。它们往往是 while (true) 或 while (!running) {} 或类似的,要么完全忽略中断,要么带有手动中断式的“运行”概念。
那么我怎么知道去哪里看呢?
足够简单:如果它是一个概念上不属于任何特定线程的事物(例如“现在有多少线程处于活动状态”),或它是一个实用程序概念(例如'sleep'),或这是一个从VM设计原则只能对你自己的线程做的事情,不能对其他任何东西做,那么它是Thread中的一个静态方法。
如果它确实属于特定线程,并且 VM 允许您对其他线程执行此操作(例如中断它、询问其名称、ID 或优先级、获取堆栈转储、冻结该线程直到另一个线程完成,或者设置它的优先级),那么它就是一个实例方法。
您可以通过多种方式反转此逻辑:如果您想做一些与线程相关的业务,请检查 Thread 类以获取似乎描述您想要的内容的内容。然后检查该方法是否是静态的。如果它是静态的,则您不能对任何其他线程执行此操作(例如清除中断标志或睡眠)。如果是实例,您可以对其他线程执行此操作(例如更改其优先级)。