终于回来"发生了吗?"

Jus*_*tin 29 java multithreading try-catch-finally

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

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

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

线程1:

public Batch fetch() {
    try {
        log("fetch()+");
        return queryDatabase();
    }
    finally {
        log("fetch()-");
    }
     ...
    workQueue.put(fetch());
Run Code Online (Sandbox Code Playgroud)

线程2:

log("take()+");
Batch b = workQueue.take();
log("take()-");
Run Code Online (Sandbox Code Playgroud)

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

124 ms : take()+
224 ms : fetch()+
244 ms : take()-
254 ms : fetch()-
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*nan 20

呼叫首先queryDatabase()发生.然后是最后一块.然后控制离开函数(就是那个return).


gco*_*ney 18

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

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

可以看出,返回语句总是突然完成.

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

  • @Justin:日志可能内置了一些延迟.我认为你只在队列上同步,而不是日志. (2认同)