终于回来“发生了吗?”

时间:2011-04-18 21:00:08

标签: java multithreading try-catch-finally

我试图说服自己在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不完全相同。我不是在询问将返回什么,而是询问内存一致性和执行顺序。

4 个答案:

答案 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-”。在您的示例中,它是多线程的,因此您不确定首先会发生什么。