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

Ste*_*fan 2 java methods multithreading java-threads

Threadclass 有许多按类名调用的静态方法。他们之中有一些是: 在此处输入图片说明

但是,我们提供了currentThread()返回当前正在执行的线程对象的方法。一些是: 在此处输入图片说明

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

我的意思是,他们不能都归为同一个“呼叫”吗?例如,为什么使用sleep() 静态而不是实例方法调用Thread.currentThread().sleep()?另一个奇怪的例子是在不同的方式之间interrupted()isInterrupted()定义的。他们做完全一样的事情,只是interrupted()另外清除了中断标志。有没有人对此有逻辑回答,所以我毫不费力地在哪里找到每种方法?

rzw*_*oot 5

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

线程睡眠

想象一下我打电话给:someOtherThread.sleep(1000L);。这意味着什么?当然,这应该意味着:睡眠其他线程,而不是我的线程。除了这不是 java 提供的东西:你可以让你自己的线程休眠,但你不能随意告诉其他线程冻结,就像他们在做一个 mime 行为,在执行一些任意命令的过程中。例如,如果该线程当前被阻塞,例如,等待操作系统从文件读取中传递一些字节,那绝对不能只是睡着了,并且还有许多其他线程无法做到这一点的情况。

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

第一个是让 sleep 成为一个实例方法(因此,您必须编写 eg Thread.currentThread().sleep(1000L);),并指定它将保证的方法,IllegalStateException如果您在除您自己的线程之外的任何线程上调用它,总是立即抛出 an 。这意味着编译/写入时可检测的错误条件只会在运行时被捕获(这很糟糕;早点捕获问题显然比晚点捕获要好),它使您必须编写的代码不必要地休眠更长时间,并且您可以在线程实例上调用 sleep 方法的存在肯定表明您可以让其他线程休眠。这只是糟糕的 API 设计。

二是让睡眠静止。

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

打断

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

之所以有两种方法,是因为中断系统或多或少的预期 API 设计。

中断系统设计如下:

如果您希望某个线程出于某种未指定的原因停止它正在执行的操作(例如,您希望它重新检查某些条件,或者停止运行,或者您能想到的任何其他事情),那么您需要一种机制来发出信号. 特别是,您需要这样一种机制来确保任何可中断的阻塞操作,例如Thread.sleep(100000L)被中断。换句话说,你不能只是说:无论如何,这取决于代码本身,只是,嗯,制作一个AtomicBoolean并检查很多。

这就是“中断”系统的用武之地。这个想法是:

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

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

  3. 因此,任何知道“我被打断了!”的代码 意味着两者都应该检查标志(特别是如果该代码具有事件循环,大多数基于线程的代码确实具有),并从任何指定抛出它的方法中捕获 InterruptedException,并以完全相同的方式做出反应以捕获该异常或有Thread.interrupted()返回true。

如果你处理中断标志上升的事实,事情就会出现各种各样的错误,但你没有降低它。例如,如果你中止你的 CPU-bound 比特币挖掘或诸如此类的东西,并在保持标志不变的情况下返回给你的调用者,那么下一次调用者调用 Thread.sleep 时,thread.sleep 会注意到这个标志被激活并立即退出,根本不睡觉(通过抛出 InterruptedException 退出,具体来说)。这不是故意的。因此,为什么在响应中断时降低该标志很重要。

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

假设设计A

while (!Thread.currentThread().isInterrupted()) {
    mineAnotherBitCoin();
}
Thread.currentThread().clearInterruptFlag();
Run Code Online (Sandbox Code Playgroud)

设计乙

while (!Thread.checkAndClearInterruptFlag()) {
   mineAnotherBitCoin();
}
Run Code Online (Sandbox Code Playgroud)

请注意,设计 B 在概念上如何更短,在检查标志和清除标志之间没有“间隙”,因此从根本上来说更不容易出错。此外,出于种种原因,已经决定提升中断标志是您可以对其他线程执行的操作(毕竟,中断自己没有意义),但是清除中断标志是您只能对自己的线程执行的操作线。

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

从根本上说的话,而他们真正的声音相似,isInterrupted()并且interrupted()做了两个非常不同的事情。

isInterrupted() 是检查某个线程是否已经被中断,并且它对该中断的响应仍然未决(尚未处理它)。

interrupted() 是您在 while 循环中放入条件的东西,它定义了线程实现的核心主体(您的“事件循环”)。

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

那我怎么知道去哪里找呢?

足够简单:如果它是一个概念上不属于任何特定线程的事物(例如“现在有多少线程处于活动状态”),或者它是一个实用程序概念(例如“睡眠”),或者它是一个事物从VM的设计原则来看,只能对自己的线程进行,不能对其他任何事情进行,那么它就是Thread中的静态方法。

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

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