If I have a function with a try/finally section, and the thread running it is interrupted while in the try block, will the finally block execute before the interruption actually occurs?
问题:
回答1:
According to the Java Tutorials, "if the thread executing the try
or catch
code is interrupted or killed, the finally
block may not execute even though the application as a whole continues."
Here's the full passage:
The
finally
block always executes when thetry
block exits. This ensures that thefinally
block is executed even if an unexpected exception occurs. Butfinally
is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by areturn
,continue
, orbreak
. Putting cleanup code in afinally
block is always a good practice, even when no exceptions are anticipated.Note: If the JVM exits while the
try
orcatch
code is being executed, then thefinally
block may not execute. Likewise, if the thread executing thetry
orcatch
code is interrupted or killed, thefinally
block may not execute even though the application as a whole continues.
class Thread1 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("finally executed");
}
}
}
...
t1.start();
t1.interrupt();
It prints - finally executed
回答2:
A Thread Interrupt in Java is just setting a flag. It doesn't cause anything special to happen to currently executing code, or affect the flow of control.
If your thread is engaged in, or attempts to enter, an operation that throws InterruptedException, then the exception is thrown from the point where that method is invoked and if it's inside a try block, the finally will execute before the exception leaves just like normal.
回答3:
Many of Oracle's Java tutorials are helpful (I have answers referencing the guarded blocks page and the SAX introduction), but they are not necessarily authoritative, and some of them have mistakes or are incomplete. The quote referenced in the question conflates interruption with the JVM exiting, which is confusing.
First, thread interruption in Java has nothing to do with OS-level interrupts. Sharing a name creates opportunities for confusion but there is no connection.
Next, JVM exit obviously kills the thread without an opportunity to do any cleanup. If the process dies before the thread has gotten as far as the finally block, too bad. But there's no comparison to interruption. Nothing about interruption prevents finally blocks from completing.
A design principle of interruption is that acting on the interruption requires the cooperation of the thread being interrupted. The thread interrupted responds at its discretion, the interruption doesn't compel the thread to do anything. All calling Thread#interrupt() does is set a flag on the thread. Blocking methods like wait or sleep check the flag to see if they should wake up early. (InterruptedException is a checked exception so you can tell who throws it when, and your Runnable can plan for it.) Also any code can use Thread#isInterrupted() to check whether its thread has had the flag set.
When Thread#sleep() recognizes the interrupted flag is set, it clears the flag before throwing InterruptedException. When your thread catches an InterruptedException it's good manners to restore the flag using Thread.currentThread().interrupt(), just in case there is any other code running in that thread that needs to know about the interruption. This comes into play when you have more complex situations with nested synchronizers where, for instance, some deeply nested component could get its sleep interrupted, letting it stay cleared could prevent higher layers from knowing about the interruption. In a simple toy example like the ones in other answers here, it doesn't matter if the flag is restored or not, nothing checks it again and the thread terminates.
回答4:
In the comments to the answer, @Risadinha asked very valid question about whether code in finally
block gets executed if we restore interruption flag inside catch
block by calling Thread.currentThread().interrupt()
.
Here is small code snippet to test:
final SomeContext context = new SomeContext();
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// this code gets executed even though
// we interrupt thread in catch block.
context.value = 9;
}
}
};
thread.start();
thread.interrupt();
thread.join(); // need to wait for thread code to complete
assertEquals(context.value, 9); // values are the same!
SomeContext class code:
class SomeContext {
public volatile int value = 10;
}
回答5:
The effect of interruption is to throw an InterruptedException
the next time a blocking operation occurs (in practice, the next time a method is called that specifies it can throw an InterruptedException
), at which point -- as usual -- the normal try/catch
execution flow is followed, which does indeed execute the finally
block after the try
and any applicable catch
es.
回答6:
It will execute the same way as with any other exception from the try block, not before the interruption.