为什么Thread类有静态方法,而我们已经有了currentThread()方法呢?

huangapple go评论75阅读模式
英文:

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

问题

Thread 类有许多静态方法,可以通过类名调用。其中一些是:

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

不幸的是,这让我感到困惑。当我想到一个我想要的方法时,我不知道我是否会将它找到作为 静态 方法还是 实例 方法。所以为什么他们要采用这两种不同的方法呢?

我的意思是,他们不能都归为同一种“调用”方式吗?例如,为什么 sleep()静态 的,而不是通过 Thread.currentThread().sleep() 调用的 实例 方法呢?另一个奇怪的例子是 interrupted()isInterrupted(),它们以不同的方式进行定义。它们实际上做了完全相同的事情,只是 interrupted() 还会清除中断标志。是否有逻辑的答案来解释这个问题,这样我就不会为了找到每个方法而苦恼呢?

英文:

Thread class has many static methods that are called by class name. Some of them are:
为什么Thread类有静态方法,而我们已经有了currentThread()方法呢?

But yet, we are provided with method currentThread() that returns currently executing thread object. Some are:
为什么Thread类有静态方法,而我们已经有了currentThread()方法呢?

Unfortunately, this created confusion in my head. When I think of a method I want, I have no clue whether I would found it as static or instance. So why did they make such two approaches?

I mean, couldn't they all be grouped in same 'calling'? For example why is sleep() static and not instance method called with Thread.currentThread().sleep()? Another weird example is between interrupted() and isInterrupted() defined in different manner. They do exactly the same thing, just interrupted() additionally clears interrupted flag. Has anyone logic answer to this, so I have no struggle where to find each method?

答案1

得分: 7

代码部分不需要翻译,以下是您提供的翻译内容:

很复杂;对于每种方法,答案都不同。让我们逐一讨论您提到的方法:

Thread.sleep

想象一下,我调用了:someOtherThread.sleep(1000L);。这意味着什么?肯定应该是:让那个其他线程休眠,而不是我的线程。但事实并非如此,Java不提供这种功能:您可以让自己的线程休眠,但您不能随意告诉其他线程像表演默剧一样冻结,中断执行任意命令。例如,如果该线程当前被阻塞,等待操作系统从文件读取中传递一些字节,那么它绝对不能只是入睡,还有许多其他情况下线程不能这样做。

因此,Java 不提供此功能 - 您不能休眠其他线程,只能休眠自己的线程。在API设计中,有两种不同的方式可以使这点至少在某种程度上清晰明了:

第一种是将休眠作为实例方法(因此,您必须编写例如 Thread.currentThread().sleep(1000L);),并且规定该方法将保证,无论何时何地在除自己线程以外的任何线程上调用它,都会立即抛出 IllegalStateException。这意味着在编译/编写时,可检测到的错误条件只会在运行时捕获(这是不好的;早期捕获问题显然比晚期捕获问题要好),这使得您需要编写的休眠代码变得不必要地更长,并且可以在线程实例上调用的休眠方法的存在确实 暗示 您可以休眠其他线程。这只会是糟糕的API设计。

第二种是使休眠成为静态方法。

这样想:java.lang.Thread 是两组基本无关的方法的容器:一组是您可以在线程上使用的方法(这些将是实例方法)。另一组是一些与线程和流相关的原语,例如 sleepyield 和中断交互。它们碰巧被塞到了同一个类中。

中断

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

之所以有两种方法,是因为中断系统的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在概念上要简短得多,没有在检查标志和清除标志之间存在“间隙”,因此从根本上说更不容易出错。此外,由于某些原因,已经决

英文:

It's tricky; the answer is different for each method. Let's go through the ones you named:

Thread.sleep

Imagine I called: someOtherThread.sleep(1000L);. What would this mean? Surely that ought to mean: Sleep that other thread, not my thread. Except that's not something java offers: You can sleep your own thread, but you cannot arbitrarily tell some other thread to freeze like they're doing a mime act, mid execution of some arbitrary command. For example, if that thread is currently blocked on, say, waiting for the OS to deliver some bytes from a file read, that definitely cannot just fall asleep, and there are many, many other scenarios where a thread cannot do that.

Thus, java does not offer this functionality - you can't sleep other threads. Only your own. There are two different ways to make this at least somewhat clear in API design:

The first is to have sleep be an instance method (thus, you'd have to write e.g. Thread.currentThread().sleep(1000L);), and spec the method that it will guaranteed, always, immediately throw an IllegalStateException if you invoke it on any thread except your own. This means a compile/write-time detectable error condition would only be caught at runtime (this is bad; catching a problem earlier is obviously better than catching it later), it makes the code you'd have to write to sleep needlessly longer, and the existence of a sleep method you can invoke on thread instances sure suggests that you can sleep other threads. It'd just be crappy API design.

The second is to make sleep static.

Think of it this way: java.lang.Thread is a container for two mostly unrelated batches of methods: One is a set of methods you can use on threads (those'd be the instance methods). The other is a bunch of thread and flow related primitives, such as 'sleep', 'yield', and interrupt interaction. They just happen to be shoved into the same class.

interrupt

This is probably the trickiest. Unlike sleeping, you can in fact ask another thread's interrupt flag status.

The reason there are two methods are because of the more or less intended API design of the interrupt system.

The interrupt system is designed as follows:

If you want some thread to stop what it is doing for some unspecified reason (for example, you want it to re-check some condition, or just cease running, or anything else you can think of) then you need a mechanism to signal this. In particular, you'd want such a mechanism to ensure that any interruptable blocking operations, such as Thread.sleep(100000L) are interrupted. In other words, you can't just say: Whatever, it's up to the code itself, just, um, make an AtomicBoolean and check it a lot.

That's where the 'interrupt' system comes in. The idea is:

  1. To interrupt any thread, raise its interrupt flag, with thatThread.interrupt();

  2. All methods that do interruptable things should check this flag. The procedure is: If it is raised, then [A] clear it, and [B] handle the interruption, doing whatever the programmer intended to happen upon interruption (just stop running, or re-check some condition, re-read some config file, who knows - it's programming, whatever you want it to mean). If you CAN handle the notion of aborting some operation, but you CANNOT handle it, then instead clear that flag and throw InterruptedException, so that the caller can handle it.

  3. As a result, any code that knows what 'I was interrupted!' means should BOTH check the flag (especially if that code has an event loop, which most thread-based code does have), AND catch InterruptedException from any method specced to throw it, and react in the exact same way to either catching that exception or having Thread.interrupted() return true.

Things go all sorts of wrong if you handle the fact that the interrupt flag is up, but you do NOT lower it. For example, if you abort your CPU-bound bitcoin mining or whatnot and just return back to your caller whilst leaving the flag up, then the next time caller invokes Thread.sleep, thread.sleep will notice the flag is up and IMMEDIATELY exit, not sleeping at all (exit by throwing InterruptedException, to be specific). That isn't intended. Hence why it is important that if you respond to an interrupt, you lower that flag.

So, let's go back to API design. There are two strategies:

Hypothetical design A

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

Design B

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

Note how design B is conceptually a lot shorter, does not have a 'gap' between checking the flag and clearing it, and therefore is fundamentally less error prone. Furthermore, for, well, reasons, it has been decided that raising an interrupt flag is something you can do to other threads (there is no point interrupting yourself, after all), but clearing one is a thing you can only do to your own thread.

B is what java actually has, except the method is somewhat strangely named interrupted(), and not checkAndClearInterruptFlag(). If you want an explanation of why some methods in java are somewhat suspectly named, it's because java does not like breaking backwards compatibility.

Fundamentally then, while they sound real similar, isInterrupted() and interrupted() do two very different things.

isInterrupted() is to check if some thread has already been interrupted and its response to this interruption is still pending (nothing has yet handled it).

interrupted() is something you put in the condition in your while loops that define the core body of your thread implementation (your 'event loop').

*) It doesn't help that the vast majority of examples of how to make threads in java are erroneous in that they don't properly do this. They tend to be while (true) or while (!running) {} or similar, either ignoring interruptions entirely or with a handrolled interrupt-esque 'running' concept.

So how do I know where to look?

Simple enough: If it's a thing that conceptually doesn't belong to any particular thread (such as 'how many threads are active right now'), or it is a utility concept (such as 'sleep'), or it is a thing that from a VM design principle can only be done to your own thread and not to anything else, then it is a static method in Thread.

If it's a thing that does belong to a particular thread AND the VM would let you do it to other threads (such as interrupting it, asking for its name, id, or priority, getting a stack dump, freezing this thread until the other thread completes, or setting its priority), then it's an instance method.

In many ways you can reverse this logic: If you want to do some thread related business, check the Thread class for something that seems to describe what you want. Then check if the method is static or not. If it is static, you don't get to do it to any other thread (such as clearing the interrupt flag, or sleep). If it's instance, you CAN do that to other threads (such as changing its priority level).

答案2

得分: 2

因为你无法使另一个不是当前线程的线程进入休眠状态。即使你调用Thread.currentThread().sleep(),你也在调用静态方法sleep。如果你试图在另一个Thread对象上调用sleep方法,它仍然会使当前线程进入休眠状态。

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

英文:

Because you can't make another thread sleep that is not the thread you are on. Even when you call Thread.currentThread().sleep(), you are calling the static method 'sleep'. If you were to call the sleep method on a different Thread object, it would still make the current thread sleep.

If you want to make a different thread sleep, you should set a flag that the other thread reads, which causes it to sleep.

huangapple
  • 本文由 发表于 2020年10月8日 11:23:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/64255319.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定