java 最后返回“发生在”之后吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5708834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 12:24:55  来源:igfitidea点击:

Does return "happen after" finally?

javamultithreadingtry-catch-finally

提问by Justin

I am trying to convince myself that actions taken in the finallyclause happen beforethe 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 beforerelationship -- if ahappens bin the program order then ahappens beforeb.

我试图说服自己在finally子句中采取的行动发生在函数返回之前(在内存一致性意义上)。从JVM规范,很显然,在一个线程中,程序顺序应该是驱动之前发生关系-如果一个发生b按照程序顺序,然后前发生b

However, I have not seen anything explicitly stating that finally happens beforereturn, so does it? Or, is there some way that the compiler could reorder the finallyclause since it is simply logging.

但是,我没有看到任何明确说明最终返回之前发生的事情,是吗?或者,编译器是否可以通过某种方式对finally子句重新排序,因为它只是记录日志。

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- finallyblocks for event timing, and I am seeing after affects of the return beforethe log statement

激励示例:我有一个线程从数据库中提取对象并将它们放入 ArrayBlockingQueue,另一个线程将它们取出。我有一些try-finally用于事件计时的块,并且我在日志语句之前看到了返回的影响

Thread 1:

主题 1:

public Batch fetch() {
    try {
        log("fetch()+");
        return queryDatabase();
    }
    finally {
        log("fetch()-");
    }
     ...
    workQueue.put(fetch());

Thread 2:

主题 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.

令我惊讶的是,这以意想不到的顺序打印出来。虽然是的,不同线程中的日志语句可能会出现乱序,但至少有 20 毫秒的时间差。

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.

请注意,这与finally trump return 的问题并不完全相同。我不是在问将返回什么,而是在问内存一致性和执行顺序。

采纳答案by gcooney

@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):

@David Heffernan 有正确的答案。JLS 规范在 14.17 节中讨论了 return 语句的行为(包括它如何与 finally 块交互)。从那里复制(强调我的):

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.

带有表达式的 return 语句试图将控制权转移到包含它的方法的调用者;Expression 的值成为方法调用的值。更准确地说,执行这样的 return 语句首先评估表达式. 如果表达式的计算由于某种原因突然完成,那么 return 语句也会因为这个原因突然完成。如果表达式的计算正常完成,产生值 V,则 return 语句突然完成,原因是返回值为 V。如果表达式的类型为 float 并且不是 FP-strict (§15.4),则该值可以是浮点值集或浮点扩展指数值集(第 4.2.3 节)的元素。如果表达式的类型是 double 并且不是 FP-strict,则该值可以是 double 值集或 double-extended-exponent 值集的元素。

可以看出,return 语句总是突然完成。

前面的描述说“尝试转移控制”而不仅仅是“转移控制”,因为 如果在其 try 块包含 return 语句的方法或构造函数中有任何 try 语句(第 14.20 节),那么这些 try 语句的任何 finally 子句将在控制转移到方法或构造函数的调用者之前,按顺序执行,从最里面到最外面。finally 子句的突然完成可能会中断由 return 语句启动的控制转移。

回答by David Heffernan

The call to queryDatabase()happens first. Then the finally block. Then control leaves the function (that's the return).

调用首先queryDatabase()发生。然后是finally块。然后控制离开函数(即return)。

回答by Will Marcouiller

The finallyclause shall execute no matter what is the result or behaviour of the tryblock, so the finallygets executed before the return.

finally无论try块的结果或行为是什么,该子句都应执行,因此finallyreturn.

回答by Marcos Vasconcelos

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.

如果你只使用一个线程,你应该看到“take+, fetch+, fetch-, take-”。在您的示例中,它是多线程的,因此您不确定首先会发生什么。