I am trying to convince myself that actions taken in the finally
clause happen before the function return (in the memory consistency sense). From the JVM specification, it is clear that within a thread, program order is supposed to drive the happens before relationship -- if a happens b in the program order then a happens before b.
However, I have not seen anything explicitly stating that finally happens before return, so does it? Or, is there some way that the compiler could reorder the finally
clause since it is simply logging.
Motivating example: I have one thread fetching objects out of a database and am putting them into an ArrayBlockingQueue, and another thread is taking them out. I have some try
- finally
blocks for event timing, and I am seeing after affects of the return before the log statement
Thread 1:
public Batch fetch() {
try {
log("fetch()+");
return queryDatabase();
}
finally {
log("fetch()-");
}
...
workQueue.put(fetch());
Thread 2:
log("take()+");
Batch b = workQueue.take();
log("take()-");
To my great surprise this prints out in an unexpected order. While, yes, logging statements in different threads can appear out of order, there is a time difference of at least 20 ms.
124 ms : take()+
224 ms : fetch()+
244 ms : take()-
254 ms : fetch()-
Note this is not exactly the same question as does finally trump return. I'm not asking what will be returned, but instead about memory consistency and execution order.
@David Heffernan has the correct answer. The JLS specification talks about the behavior of the return statement(including how it interacts with finally blocks) in section 14.17. Copying from there (emphasis mine):
A return statement with an Expression attempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation. More precisely, execution of such a return statement first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the return statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the return statement completes abruptly, the reason being a return with value V. If the expression is of type float and is not FP-strict (§15.4), then the value may be an element of either the float value set or the float-extended-exponent value set (§4.2.3). If the expression is of type double and is not FP-strict, then the value may be an element of either the double value set or the double-extended-exponent value set.
It can be seen, then, that a return statement always completes abruptly.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.
The call to queryDatabase()
happens first. Then the finally block. Then control leaves the function (that's the return
).
The finally
clause shall execute no matter what is the result or behaviour of the try
block, so the finally
gets executed before the return
.
If you are using only one thread, you should see "take+, fetch+, fetch-, take-". In your example, it's multi-threaded, so you are not sure what happens first.
来源:https://stackoverflow.com/questions/5708834/does-return-happen-after-finally