JCIPのセクション3.2.1「安全なコンストラクターの実践」ではthis
、「パブリケーションがコンストラクターの最後のステートメントであっても」、コンストラクターから別のスレッドにリークすることに対する警告があります。その最後の部分は私には強すぎるように思えます、そしてそれは正当化されていません。建設後に何が起こるので、私は避けるように注意しなければなりませんか?例外はありますか?私は最近、これを実行したコードを提出したので興味があります。戻ってリファクタリングする正当な理由があるかどうかを判断したいと思います。
3 に答える
this
「最後のステートメントの[...]でさえ」、どの時点でもコンストラクターからリークしてはなりません。this
完全に構築されていないため、いくつかの非常に奇妙なことが起こる可能性があります。非常によく似た質問については、このSOの回答を参照してください。
コンストラクターから抜け出してはいけませんthis
(「リークthis
」と呼ばれます)
コンストラクターの最後の行であっても、これを実行しない理由の1つは、現在のスレッドへの影響が影響を受けない限り、JVMがステートメントを並べ替えることができるためです。this
別のスレッドで実行されているプロセスに渡された場合、並べ替えによって奇妙で微妙なバグが発生する可能性があります。
もう1つの理由は、サブクラスが独自の初期化を提供する可能性があるため、クラスのコンストラクターの最後の行で構築が完了しない可能性があることです。
Javaメモリモデルに関する限り、コンストラクター出口は最終フィールドのセマンティクスで役割を果たします。したがって、ステートメントがコンストラクター出口の前であるか後であるかには違いがあります。
This works This doesn't work
-------------------------------------------------------------
static Foo shared; static Foo shared;
class Foo class Foo
{ {
final int i; final int i;
Foo() Foo()
{ {
i = 1; i = 1;
shared = this;
} }
} }
shared = new Foo(); new Foo();
(注:shared
揮発性ではありません。公開はデータレースを通じて行われます。)
2つの例の唯一の違いは、shared
コンストラクターの終了前または終了後に割り当てることです。2番目の例でi=1
は、割り当て後に並べ替えることができます。
ただし、パブリケーションが同期アクションである場合、たとえば揮発性変数を介した場合は、問題ありません。他のスレッドは完全に初期化されたオブジェクトを監視します。フィールドはする必要はありませんfinal
。
データレースを介した公開(またはデータレースを介した何かの実行)は非常にトリッキーなビジネスであり、非常に慎重な推論が必要です。データの競合を回避すると、事態ははるかに簡単になります。コードにデータ競合が含まれていない場合、コンストラクター終了直前のリークとコンストラクター終了直後の公開に違いはありません。this