我试图说服自己在finally
子句中采取的操作发生在函数返回之前(在内存一致性意义上)。从JVM specification可以看出,在一个线程中,程序顺序应该驱动在关系之前发生 - 如果 a 发生 b < / strong>在程序顺序中 a 发生在 b 之前。
但是,我没有看到任何明确声明最终在返回之前发生的事情,所以它呢?或者,编译器是否可以通过某种方式重新排序finally
子句,因为它只是记录。
激励示例:我有一个线程从数据库中取出对象并将它们放入ArrayBlockingQueue,另一个线程将它们取出。我有一些try
- finally
块用于事件计时,我看到在返回之后日志语句的影响
主题1:
public Batch fetch() {
try {
log("fetch()+");
return queryDatabase();
}
finally {
log("fetch()-");
}
...
workQueue.put(fetch());
主题2:
log("take()+");
Batch b = workQueue.take();
log("take()-");
令我惊讶的是,这是以意想不到的顺序打印出来的。虽然,是的,不同线程中的日志记录语句可能出现故障,但时间差至少为20毫秒。
124 ms : take()+
224 ms : fetch()+
244 ms : take()-
254 ms : fetch()-
请注意,这与does finally trump return不完全相同。我不是在询问将返回什么,而是询问内存一致性和执行顺序。
答案 0 :(得分:20)
首先调用queryDatabase()
。然后是最后一块。然后控制离开函数(即return
)。
答案 1 :(得分:17)
@David Heffernan有正确的答案。 JLS规范在第14.17节中讨论了return语句的行为(包括它与finally块的交互方式)。从那里复制(强调我的):
带有Expression的return语句 试图将控制转移到 包含的方法的调用者 它;表达式的值 成为方法的价值 调用。 更确切地说,执行 首先是这样的退货声明 评估表达式。如果 表达式的评估完成 突然出于某种原因,那么 return语句突然完成 是因为。如果评价了 表达正常完成, 产生一个值V,然后返回 声明突然完成, 理由是价值回报V.如果 表达式是float类型,是 不是FP-strict(§15.4),那么是值 可能是浮动的元素 价值集或 float-extended-exponent值设置 (§4.2.3)。如果表达式是类型 加倍并且不是FP严格的,那么 值可能是其中一个元素 双值设置或 double-extended-exponent value set。
然后,可以看出,回归 声明总是突然完成。
前面的描述说 “尝试转移控制”而非 而不只是“转移控制”,因为 如果有任何try语句 (§14.20)方法或 try块包含的构造函数 返回语句,然后任何最终 那些尝试陈述的条款将 按顺序执行,最内层到 最外层,在控制之前 调到了调用者 方法或构造函数。突兀 完成一个finally子句即可 扰乱控制权的转移 由退货声明发起。
答案 2 :(得分:3)
无论finally
块的结果或行为是什么,都应执行try
子句,因此finally
会在return
之前执行。
答案 3 :(得分:2)
如果您只使用一个线程,则应该看到“take +,fetch +,fetch-,take-”。在您的示例中,它是多线程的,因此您不确定首先会发生什么。