web-dev-qa-db-ja.com

ついに「後」に戻るのか?

finallyで実行されたアクションが、関数が(メモリの一貫性の意味で)返される前に発生することを確信しようとしています。 JVM仕様 から、スレッド内では、プログラムの順序がhappens before関係を駆動することになっていることは明らかです-ifaは、プログラムの順序でb発生し、a前に起こりますb

しかし、私は最終的にが戻る前に起こることを明示的に述べているものを見たことがないので、そうですか?または、単にロギングしているだけなので、コンパイラがfinally句を並べ替えることができる方法はありますか。

やる気を起こさせる例:1つのスレッドがデータベースからオブジェクトをフェッチし、それらをArrayBlockingQueueに入れており、別のスレッドがそれらを取り出しています。イベントのタイミングにtry- finallyブロックがいくつかあり、リターンの影響後beforeが表示されます。ログステートメント

スレッド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()-

これは 最終的にトランプが返される とまったく同じ質問ではないことに注意してください。何が返されるかではなく、メモリの一貫性と実行順序について質問します。

30
Justin

@DavidHeffernanが正解です。 JLS仕様では、セクション14.17でr​​eturnステートメントの動作(finallyブロックとの相互作用を含む)について説明しています。そこからコピーする(私の強調):

式を含むreturnステートメントは、それを含むメソッドの呼び出し元に制御を移そうとします。式の値は、メソッド呼び出しの値になります。 より正確には、このようなreturnステートメントの実行は、最初に式を評価します。式の評価が何らかの理由で突然完了した場合、returnステートメントはその理由で突然完了します。式の評価が正常に完了し、値Vが生成されると、returnステートメントが突然完了します。理由は値Vの戻りです。式がfloat型であり、FP-strict(§15.4)でない場合、値はfloat値セットまたはfloat-extended-exponent値セット(§4.2.3)のいずれかの要素である可能性があります。式がdouble型であり、FP-strictでない場合、値はdouble値セットまたはdouble-extended-exponent値セットのいずれかの要素である可能性があります。

したがって、returnステートメントは常に突然完了することがわかります。

上記の説明では、tryブロックにreturnステートメントが含まれているメソッドまたはコンストラクター内にtryステートメント(§14.20)がある場合、単に「制御を転送する」ではなく「制御を転送しようとする」と述べています。次に、これらのtryステートメントのfinally句は、メソッドまたはコンストラクターの呼び出し元に制御が移される前に、最も内側から最も外側の順に実行されます。 finish句が突然完了すると、returnステートメントによって開始された制御の転送が中断される可能性があります。

18
gcooney

queryDatabase()の呼び出しが最初に行われます。その後、最後にブロックします。次に、コントロールは関数を終了します(つまり、return)。

20
David Heffernan

finally句は、tryブロックの結果や動作に関係なく実行されるため、finallyreturnの前に実行されます。

3

スレッドを1つだけ使用している場合は、「take +、fetch +、fetch-、take-」と表示されます。あなたの例では、それはマルチスレッドであるため、最初に何が起こるかわかりません。

2