为什么无法使用Thread.currentThread().interrupted()来调用Thread.interrupted()?

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

Why is Thread.interrupted() not possible to call with Thread.currentThread().interrupted?

问题

我决定重新审查一些旧的多线程概念。我遇到了中断方法。我阅读了关于方法isInterrupted()interrupted()之间区别的内容。关键在于interrupted()会重置标志,而isInterrupted()则不会。

我开始编码,我注意到以下情况。方法interrupted()必须与Thread的'前缀'一起使用,而方法isInterrupted()必须与Thread.currentThread()的'前缀'一起使用。我想知道为什么。为了演示:

Thread.interrupted(); //编译通过
Thread.currentThread().interrupted(); //不能编译通过

Thread.currentThread().isInterrupted(); //编译通过
Thread.isInterrupted(); //不能编译通过

总之,有人可以告诉我为什么它们有不同的调用方式吗?Thread.Thread.currentThread()之间有什么区别?我的意思是,当我调用Thread.时,它应该直接'聚焦'到那个线程对象,所以我不明白在Java中为什么要随处使用Thread.currentThread()。有人可以帮我澄清一下吗?

英文:

I decided to revise some old multi-threading concepts. I came across interrupt methods. I read about difference between methods isInterrupted() and interrupted(). Point is interrupted() resets the flag, isInterrupted() doesn't.

I started coding and I noticed following. Method interrupted() must be used with 'prefix' of Thread, while method isInterrupted() must be used with 'prefix' of Thread.currentThread(). I wonder why. To demonstrate:

Thread.interrupted(); //compiles
Thread.currentThread().interrupted(); //doesn't compile

Thread.currentThread().isInterrupted(); //compiles
Thread.isInterrupted(); //doesn't compile

To sum, can someone tell me why do they have different calls? And what is difference between Thread. and Thread.currentThread()? I mean when I call Thread. it should directly 'focus' everything to that thread object, so I see no point of using Thread.currentThread() anywhere in Java. Can someone please clear my confusion?

答案1

得分: 1

interrupted() 是一个静态方法。

对于静态方法,通常的调用方式如下:

TypeTheMethodIsIn.staticMethod();

与之相对,非静态方法只能像这样调用:

(包含方法的对象类型的表达式).instanceMethod();

从技术上讲,你确实可以以第二种方式调用静态方法,但这是完全没有意义的(类型不会进行运行时查找),而且所有的代码检查工具,包括 javac 本身,都会告诉你停止这样做。你应该遵循这个建议。

Thread 的 interrupted() 方法是静态的。Thread 的 isInterrupted() 方法不是。

要点应该很清楚:你可以对任何线程检查中断标志。然而,清除标志呢?你只能对自己的线程执行清除操作。

在某种程度上,我们正在深入探讨 API 程序员在编写这个 API 时的感觉和想法,对此,我和 Stack Overflow 上的任何其他人都无法给出合理的答案。但我们可以探讨一下为什么他们可能会以这种方式来设计。

鉴于你不应该修改其他线程的中断标志,只能修改自己的标志,可能是这样的:Thread.currentThread().clearInterruptedFlag(),但现在 currentThread() 是完全多余的;如果在任何其他线程上调用它,你将不得不抛出异常,以确保线程只能清除自己的标志。

另一种方法是任何线程都可以清除任何其他线程的标志,或者自己的标志,但这完全是荒谬的。提高该标志的目的是让该线程中的代码最终找到它并退出或以其他方式尽快停止某些阻塞进程。

那么,为什么没有一种轻松检查自己标志而不清除它的方法呢?

再次强调,这是 API 的设计。有什么意义呢?

以下是如何使用中断标志的方法:

  1. 如果你的线程的核心阻塞性质基于某种 CPU 冻结操作(定义为:核心库中任何被规定要抛出 InterruptedException 的操作,比如 obj.wait、Thread.sleep、yield 等等),你大多数情况下不需要做任何事情。如果在你的 CPU 上提高了中断标志,你可以完全忽略它;很快(非常快),你的线程将执行其中一个那些“让 CPU 冻结”的方法,比如 Thread.sleep,而所有这些方法的实现首先会检查标志,如果它们注意到标志被提高了,它们会立即抛出 InterruptedException,甚至不会冻结 CPU(它们也会立即降低该标志)。这应该会结束你的方法。(如果你捕获了 InterruptedException 并忽略了它,请不要那样做)。

  2. 如果你的线程性质是阻塞的,但不是规定的,那么就要看架构了。例如,如果你在等待从网络套接字读取数据,谁知道会发生什么。要么标志会被忽略,你对此无能为力,要么读取(read())调用或者你正在做的任何操作都会抛出 IOException。它不能抛出 InterruptedException —— 该方法的规范不允许这样做。标志不会被提高。

  3. 如果你的线程性质是在主动操作中花费时间,例如,你正在挖比特币,你所做的就是无休止地旋转 CPU,将哈希算法合并在一起,那么默认情况下,中断这样的线程根本不会产生任何效果。然而,这样的线程很有可能有某种 while 循环。你只需要检查中断标志。如果标志被提高,就退出。你可以决定如何处理:抛出异常,或者只是... 退出。干净地结束你的 @Override public void run() {} 的代码。嘿,你提高了那个中断,你可以决定它意味着什么。

对于情况 #3 的关键点是,无论在哪种情况下,都应该降低标志,因为如果它保持提高状态,并且你的代码不是回退到“此线程现在结束”,而是“我将控制权交还给我的调用者”,如果该标志仍然提高,那么结果将会非常令人惊讶。

因此,你最终会编写出如下的代码:

Runnable r = () -> {
    while (!Thread.interrupted()) {
        mineNextHash();
    }
};

这段代码简洁明了,非常简单。

英文:

interrupted() is a static method.

For static methods, the usual way to invoke them is like so:

TypeTheMethodIsIn.staticMethod();

contrast this to non-static methods which can only be invoked like so:

(expression of a type of the object containing the method).instanceMethod();

Technically, you CAN invoke static methods in this second fashion, but this is entirely pointless (no runtime lookup is done of the type), and all linter tools, including javac itself, will tell you to knock it off. Which you should heed.

Thread's interrupted() method is static. Thread's isInterrupted() method is not.

The point should be clear enough: Checking the interrupted flag is something you can do to any thread you like. However, clearing it? You can only do that to your own thread.

At some point we're delving into the feelings and thoughts of the programmer of the API on the day they programmed it, to which neither I nor anyone else on SO can give you a reasonable answer. But we can delve into why they MIGHT have done it this way.

Given that you are not supposed to modify the interrupted flag of other threads, only of your own, it COULD have been something like: Thread.currentThread().clearInterruptedFlag(), but now currentThread() is entirely superfluous; attempt to invoke it on any other thread and you'd then have to throw an exception, in order to ensure that a thread can only clear its own flag.

An alternative would be that any thread can clear any other thread's flag, or its own flag, but that is entirely nonsensical. The point of raising that flag is for the code in that thread to eventually stumble across it and exit or otherwise stop some blocking process ASAP.

So why is there no method to easily check your own flag without clearing it?

Again, API design. What point is there to this?

This is how you're supposed to use the interrupt flag:

  1. If the central blocking nature of your thread is based on some sort of CPU freeze operation (defined as: Anything in the core libs that is specced to throw InterruptedException, such as obj.wait, Thread.sleep, yield, etc), you mostly don't have to do anything. If the interrupt flag is raised on your CPU feel free to entirely ignore this; soon (very soon) your thread will execute one of those 'make the CPU freeze' methods, such as Thread.sleep, and the implementations of all these methods first check the flag and will immediately throw InterruptedException without ever even freezing the CPU if they notice that it is raised (and they lower that flag immediately, as well. Either the flag is up, or InterruptedEx is thrown, never both). Which should end your method. (If you are catching InterruptedException and ignoring it, don't do that).

  2. If the nature of your thread is such that it blocks but isn't specced, that it's up to the architecture. For example, if you're waiting on reading data from a network socket, who knows what is going to happen. Either the flag is ignored and there's nothing you can do about it, or, the read() call or whatever you're doing will throw IOException. It can't throw InterruptedException - the method isn't specced to allow that. The flag will NOT be up.

  3. If the nature of your thread is such that the time is spent in active operation, for example, you're mining bitcoin and all you're doing is endlessly spinning that CPU, hashing algorithms together, then out of the box interrupting such a thread does nothing whatsoever. However, such a thread is highly likely to have a while loop of some sort. All you need to do is check the interrupt flag. If it is up, exit. It's up to you how: Either throw something, or, just.. exit. End your @Override public void run() {}'s code cleanly. Hey, you raised that interrupt, you get to decide what it means.

key point for #3 is that in both cases that flag should be lowered, because if it remains raised and your code falls back not to 'this thread is now over' but 'I relinquish control back to my caller', it would be highly surprising if that flag is up.

So, you end up writing code like this:

Runnable r = () -> {
    while (!Thread.interrupted()) {
        mineNextHash();
    }
};

which is short, clean, and to the point. easy peasy.

huangapple
  • 本文由 发表于 2020年9月17日 07:11:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/63929104.html
匿名

发表评论

匿名网友

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

确定