为什么当我们有 currentThread() 方法时 Thread class 有静态方法?

Why does Thread class has static methods when we have currentThread() method?

Thread class 有许多由 class 名称调用的静态方法。他们之中有一些是:

但是,我们提供了方法 currentThread(),returns 当前正在执行线程对象。一些是:

不幸的是,这在我脑海中造成了混乱。当我想到我想要的方法时,我不知道我会发现它是 static 还是 instance。那么他们为什么要采用这两种方法呢?

我的意思是,他们不能都归为同一个 'calling' 吗?例如,为什么 sleep() static 而不是 instance 方法用 Thread.currentThread().sleep() 调用?另一个奇怪的例子是以不同方式定义的 interrupted()isInterrupted() 之间。他们做完全一样的事情,只是 interrupted() 额外清除中断标志。有没有人对此有合理的答案,所以我很容易在哪里找到每种方法?

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

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

这很棘手;每种方法的答案都不同。让我们来看看你命名的那些:

Thread.sleep

假设我调用了:someOtherThread.sleep(1000L);。这意味着什么?这当然应该意味着:休眠另一个线程,而不是我的线程。除了那不是 java 提供的东西:你可以休眠你自己的线程,但你不能任意地告诉其他线程冻结,就像他们在做一个哑剧一样,在执行一些任意命令的过程中。例如,如果该线程当前被阻塞,比如说,等待 OS 从文件读取中传送一些字节,那绝对不能只是睡着了,还有很多很多其他线程不能做的场景那。

因此,java 不提供此功能 - 您无法休眠其他线程。只有你自己的。在 API 设计中有两种不同的方法可以使这一点至少在一定程度上清楚:

首先是让 sleep 成为一个实例方法(因此,您必须编写例如 Thread.currentThread().sleep(1000L);),并指定它将保证始终立即抛出 IllegalStateException 如果您在除您自己的线程之外的任何线程上调用它。这意味着 compile/write-time 可检测到的错误条件只会在运行时被捕获(这很糟糕;较早发现问题显然比稍后发现问题要好),它使您必须编写的代码不必要地更长地休眠,以及可以在线程实例上调用的睡眠方法的存在肯定 建议 您可以睡眠其他线程。这只是糟糕的 API 设计。

二是让睡眠静止。

这样想:java.lang.Thread 是两个几乎不相关的方法批次的容器:一个是一组可以在线程上使用的方法(那些是实例方法)。另一个是一堆线程和流相关的原语,比如'sleep'、'yield'、中断交互。他们只是碰巧被推到同一个 class.

中断

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

有两种方法的原因或多或少是因为中断系统的API设计。

中断系统设计如下:

如果你想让一些线程因为一些未指明的原因停止它正在做的事情(例如,你想让它达到re-check某种条件,或者只是停止运行,或者任何你能想想)那么你需要一种机制来发出这个信号。特别是,您需要这样一种机制来确保任何可中断的阻塞操作(例如 Thread.sleep(100000L))都被中断。换句话说,你不能只是说:不管怎样,这取决于代码本身,只是,嗯,做一个 AtomicBoolean 并经常检查它。

这就是 'interrupt' 系统的用武之地。想法是:

  1. 要中断任何线程,提升其中断标志,thatThread.interrupt();

  2. 所有执行可中断操作的方法都应该检查这个标志。程序是:如果它被引发,然后 [A] 清除它,并且 [B] 处理中断,做任何程序员打算在中断时发生的事情(只是停止 运行,或 re-check 一些条件, re-read 一些配置文件,谁知道 - 它是编程,无论你想要什么意思)。如果您可以处理中止某些操作的概念,但您不能处理它,那么清除该标志并抛出 InterruptedException,以便调用者可以处理它。

  3. 因此,任何知道 'I was interrupted!' 意味着什么的代码都应该检查标志(特别是如果该代码有一个事件循环,大多数 thread-based 代码确实有), 并从指定抛出它的任何方法中捕获 InterruptedException,并以完全相同的方式做出反应以捕获该异常或使 Thread.interrupted() return 为真。

如果您处理中断标志已启动但未降低它的事实,事情就会变得很糟糕。例如,如果您中止了您的 CPU-bound 比特币挖掘或诸如此类的事情,并且只是 return 返回给您的调用者同时留下标志,那么下次调用者调用 Thread.sleep、thread.sleep会注意到标志已升起并立即退出,根本不睡觉(具体来说,通过抛出 InterruptedException 退出)。那不是故意的。因此,为什么重要的是,如果您响应中断,则降低该标志。

所以,让我们回到API设计。有两种策略:

假设设计 A

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

设计 B

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

请注意设计 B 在概念上是如何短得多的,在检查标志和清除标志之间没有 'gap',因此从根本上不容易出错。此外,出于某些原因,已经决定提高中断标志是您可以对其他线程执行的操作(毕竟没有必要中断您自己),但是清除中断标志是您只能对自己执行的操作线程。

B 是 java 实际拥有的,只是方法有点奇怪只是命名为 interrupted(),而不是 checkAndClearInterruptFlag()。如果您想解释为什么 java 中的某些方法的命名有些可疑,那是因为 java 不喜欢破坏向后兼容性。

从根本上说,虽然它们听起来很相似,但 isInterrupted()interrupted() 做了两件截然不同的事情。

isInterrupted()是检查某个线程是否已经被中断并且它对这个中断的响应还在pending(还没有处理它)。

interrupted() 是您在 while 循环中放入的条件,它定义线程实现的核心主体(您的 'event loop')。

*) java 中关于如何创建线程的绝大多数示例都是错误的,因为它们没有正确执行此操作,这无济于事。它们往往是 while (true)while (!running) {} 或类似的,要么完全忽略中断,要么使用手动 interrupt-esque 'running' 概念。

那么我怎么知道去哪里看?

足够简单:如果它是概念上不属于任何特定线程的东西(例如'how many threads are active right now'),它是一个实用概念(例如as 'sleep'), or 是VM设计原则上只能对自己线程做的事情,不能对其他任何事情做,那么就是静态方法线程。

如果它确实属于某个特定线程并且 VM 允许您对其他线程执行此操作(例如中断它、询问它的名称、ID 或优先级、获取堆栈转储、冻结它线程直到另一个线程完成,或设置其优先级),那么它就是一个实例方法。

您可以通过多种方式扭转这种逻辑:如果您想做一些与线程相关的业务,请检查线程 class 以寻找似乎描述您想要的内容的内容。然后检查该方法是否是静态的。如果它是静态的,则您无法对任何其他线程执行此操作(例如清除中断标志或睡眠)。如果是实例,您可以对其他线程执行此操作(例如更改其优先级)。