If a thread is interrupted while inside Object.wait()
or Thread.join()
, it throws an InterruptedException
, which resets the thread's interrupted status. I. e., if I have a loop like this inside a Runnable.run()
:
while (!this._workerThread.isInterrupted()) {
// do something
try {
synchronized (this) {
this.wait(this._waitPeriod);
}
} catch (InterruptedException e) {
if (!this._isStopping()) {
this._handleFault(e);
}
}
}
the thread will continue to run after calling interrupt()
. This means I have to explicitly break out of the loop by checking for my own stop flag in the loop condition, rethrow the exception, or add a break
.
Now, this is not exactly a problem, since this behaviour is well documented and doesn't prevent me from doing anything the way I want. However, I don't seem to understand the concept behind it: Why is a thread not considered interrupted anymore once the exception has been thrown? A similar behaviour also occurs if you get the interrupted status with interrupted()
instead of isInterrupted()
, then, too, the thread will only appear interrupted once.
Am I doing something unusual here? For example, is it more common to catch the InterruptedException
outside the loop?
(Even though I'm not exactly a beginner, I tagged this "beginner", because it seems like a very basic question to me, looking a开发者_运维知识库t it.)
The idea is that an interrupt should be handled once. If an explicit InterruptedException
did not clear the "interrupt" flag then most catchers for InterruptedException
would have to explicitly clear that flag. Conversely, you can "unclear" the flag by self-interruption (Thread.currentThread().interrupt()
). Java's designers went for the semantics which would save keystrokes most of the time (i.e. you more often want to clear the flag than keep it set).
It shouldn't. This is an unfortunate design flaw that makes relying on interruptions a risky business, as too often library code will catch InterruptedException
without resetting the thread's interrupted flag and carry on. If you happen to signal an interruption to your thread when that particular piece of broken library code is running, when your code regains execution control, it'll be left without a clue that the interruption happened.
This only needs to happen once in any place that you're calling from your code, so in order to be able to interrupt a thread and then use the interrupted bit to control your flow from inside said thread safely, you need to be 100% sure that every piece of code that you're calling does not clear the interrupted bit by mistake. This is very hard to do when libraries are involved, but even if you could account for every single library that you're using in your code, that still doesn't account for buggy JRE code that can make the same mistake.
The fact that it only takes one library (or JRE!) author to not care or think about interruptions in order to break the logic of code that requires it shows that this is the wrong default action to take. Someone who doesn't care about the thread's interrupted bit probably won't bother to reset it after catching InterruptedException
– maybe they don't even know it exists! If catching InterruptedException
didn't reset the thread's interrupted status, then anyone who did not know about the interrupted bit would automatically "do the right thing" and not cause a problem for any calling code relying on interruptions. Anyone who required clearing it could still do so manually, but then it'd be an explicit action which is much more likely to be correct than an usually unintended side-effect of catching the checked InterruptedException
exception. As it stands right now, if you rely on the thread's interrupted bit, anyone down your calling stack that calls Thread.sleep()
carelessly can potentially ruin your day.
As a result, most Java multi-threaded code will just duplicate the Java thread interrupt model with an "isRunning" instance field and some mechanism to flip it as a workaround.
Write your code like this and you won't need a flag:
try {
while (!this._workerThread.isInterrupted()) {
// do something
synchronized (this) {
this.wait(this._waitPeriod);
}
// do something else
}
} catch (InterruptedException e) {
// ignore ...
}
As @Boyan points out, it is a bad idea to squash that the interrupt exception ... in general. In this case, the context will determine whether you should squash it (as above), set the interrupt flag (again) or allow the exception to propagate. Among other things, it depends on what the interrupt means in / to your application.
That's because an InterruptedException
is considered an abnormal event in which someone else tries to stop a thread from outside it.
When you want to really interrupt a thread you just break its loop condition by setting a boolean or something similar. Or you use .wait()
and .notify()
from inside that thread. But if you are doing wait()
externally:
- an exception is thrown to notify that an external thread tried to interrupt me or to make me wait
- the thread continues its work because it doesn't take any order from another thread! But the raise of the exception allows you to add special handling and do whatever you want, also effectively stop the thread.
精彩评论