Возвращение "произойдет после" в конце концов? - PullRequest
29 голосов
/ 19 апреля 2011

Я пытаюсь убедить себя, что действия, предпринятые в предложении finally , происходят до возврата функции (в смысле согласованности памяти).Из спецификации JVM ясно, что в потоке программный порядок должен приводить в действие до того, как - если a происходит b в программном порядке, тогда 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()-

Обратите внимание, что это не совсем тот же вопрос, что и , наконец, превосходитвернуться .Я не спрашиваю, что будет возвращено, а о согласованности памяти и порядке выполнения.

Ответы [ 4 ]

20 голосов
/ 19 апреля 2011

Звонок на queryDatabase() происходит первым. Затем, наконец, блок. Затем управление покидает функцию (это return).

17 голосов
/ 19 апреля 2011

@ Дэвид Хеффернан имеет правильный ответ.Спецификация JLS говорит о поведении оператора return (включая то, как он взаимодействует с блоками finally) в разделе 14.17.Копирование оттуда (выделено мое):

Оператор return с выражением пытается передать управление вызывающему методу, который его содержит;значение Expression становится значением вызова метода. Точнее, выполнение такого оператора возврата сначала оценивает Выражение .Если по какой-либо причине оценка Expression завершается преждевременно, то по этой причине оператор return завершается преждевременно.Если вычисление Expression завершается нормально, производя значение V, тогда оператор return завершается преждевременно, причиной чего является возвращение со значением V. Если выражение имеет тип float и не строго FP (§15.4), тогда значениеможет быть элементом либо набора значений с плавающей запятой, либо набора значений с расширенной экспонентой с плавающей запятой (§4.2.3).Если выражение имеет тип double и не строго FP, то значение может быть элементом либо набора двойных значений, либо набора значений двойной расширенной экспоненты.

Это можно увидеть, тогдачто оператор return всегда завершается преждевременно.

В предыдущих описаниях говорится «попытки передать управление», а не просто «передавать управление», потому что если есть какие-либо операторы try (§14.20) в методе или конструкторечьи блоки try содержат оператор return, тогда любые операторы finally этих операторов try будут выполняться в порядке, от самого внутреннего до самого внешнего, перед тем, как управление будет передано вызывающему методу или конструктору .Внезапное завершение предложения finally может нарушить передачу управления, инициированную оператором return.

3 голосов
/ 19 апреля 2011

Предложение finally должно выполняться независимо от того, каков результат или поведение блока try, поэтому finally выполняется до return.

2 голосов
/ 19 апреля 2011

Если вы используете только один поток, вы должны увидеть «take +, fetch +, fetch-, take-».В вашем примере он многопоточный, поэтому вы не уверены, что произойдет первым.

...