繁体   English   中英

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

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

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

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

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

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

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

线程睡眠

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

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

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

二是让睡眠静止。

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

打断

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

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

中断系统设计如下:

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

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

  1. 要中断任何线程,请使用thatThread.interrupt();提高其中断标志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();

设计乙

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

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

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

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

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

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM