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

问题描述

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

enter image description here

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

enter image description here

不幸的是,这使我感到困惑。当我想到自己想要的方法时,我不知道是将其视为 static 还是 instance 。那么为什么他们要采用这两种方法呢?

我的意思是,难道不能将它们全部归为同一“通话”吗?例如,为什么sleep() static 而不是Thread.currentThread().sleep()调用 instance 方法?另一个奇怪的例子是在interrupted()isInterrupted()之间以不同的方式定义。它们的作用完全相同,只是interrupted()还会清除中断的标志。有人对此有逻辑上的答案,所以我不费力在哪里找到每种方法

解决方法

这很棘手;每种方法的答案都不同。让我们浏览一下您所命名的:

Thread.sleep

想象一下我打过电话:someOtherThread.sleep(1000L);。这是什么意思?当然,这应该意味着:休眠其他线程,而不是我的线程。除非Java不能提供这些功能:您可以休眠自己的线程,但不能随意告诉其他线程冻结,就像他们在执行mime动作,执行任意命令的中间一样。例如,如果该线程当前处于阻塞状态,例如,等待操作系统从读取的文件中提供一些字节,那肯定不能只是睡着了,在很多其他情况下,线程无法做到这一点。>

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

第一个方法是让sleep作为实例方法(因此,您必须编写例如Thread.currentThread().sleep(1000L);),并指定可以保证的方法,如果这样,该方法始终会立即抛出IllegalStateException您可以在除您自己的线程之外的任何线程上调用它。这意味着只能在运行时捕获编译/写入时可检测到的错误情况(这很糟糕;尽早发现问题要比晚捕获更好),这使您必须编写的代码不必要地睡得更长一些,以及您可以在线程实例上调用的sleep方法的存在,请确保建议您可以休眠其他线程。只是糟糕的API设计。

第二个是使睡眠静止。

这样想:java.lang.Thread是两个几乎不相关的方法批次的容器:一个是可以在线程上使用的一组方法(那些是实例方法)。另一个是一堆与线程和流相关的原语,例如“睡眠”,“产量”和中断交互。他们恰好被推到同一个班级。

中断

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

之所以有两种方法,是因为中断系统或多或少打算使用API​​设计。

中断系统的设计如下:

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

这就是“中断”系统的所在。这个想法是:

  1. 要中断任何线程,请使用thatThread.interrupt();

    升高其中断标志。
  2. 所有执行可中断操作的方法都应检查此标志。过程是:如果引发该异常,则[A]清除它,然后[B]处理中断,执行程序员打算在中断时发生的任何事情(只需停止运行,或重新检查某些条件,重新读取一些配置文件,谁知道-它是编程,无论您想要什么。如果您可以处理中止某些操作的概念,但不能处理它,那么请清除该标志并抛出InterruptedException,以便调用者可以处理它。

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

如果您处理了中断标志已建立的事实,但您没有降低它,事情会出错。例如,如果您中止CPU绑定的比特币挖掘或其他操作,而只是在返回标志的同时返回到调用方,则下次调用方调用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) {}或类似名称,要么完全忽略中断,要么使用手动中断式“运行”概念。

那我怎么知道去哪里看?

足够简单:如果从概念上讲它不属于任何特定线程(例如“当前有多少个线程处于活动状态”),则就是一个实用程序概念(例如'),,这是从VM设计原则出发,只能对自己的线程执行的操作,不能对其他任何事情执行,这是Thread中的静态方法。

如果它确实属于特定线程,并且VM允许您将其用于其他线程(例如中断它,询问其名称,ID或优先级,获取堆栈转储,冻结该线程直到另一个线程完成或设置其优先级),则它是一个实例方法。

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

,

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

如果要使其他线程进入睡眠状态,则应设置另一个线程读取的标志,这会使该线程进入睡眠状态。