终于回来“发生了吗?”

我试图说服自己, finally子句中的操作发生在函数返回之前 (在内存一致性意义上)。 从JVM规范中可以清楚地看出,在一个线程中,程序顺序应该关系之前驱动发生 – 如果在程序顺序中发生b b 之前发生

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

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

线程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()- 

请注意,这与最终特朗普回归的问题并不完全相同。 我不是在询问将返回什么,而是询问内存一致性和执行顺序。

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

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

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

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

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

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

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