ダブルチェックロック
-
03-07-2019 - |
質問
Doubleについてこの記事を読んでいた-ロックの確認"記事のメイントピックから、記事のある時点で著者が次のイディオムを使用する理由を疑問に思いました:
リスト7.順不同の書き込み問題を解決しようとする
public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 inst = new Singleton(); //4 } instance = inst; //5 } } } return instance; }
そして私の質問は: 同じロックでいくつかのコードを2回同期する理由はありますか? これには何か目的がありますか?
事前に感謝します。
解決
2回ロックするポイントは、順不同の書き込みを防ぐために試行することでした。メモリモデルは、部分的にロックの観点から並べ替えが発生する場所を指定します。このロックにより、" instance = inst;"の後に書き込み(シングルトンコンストラクター内を含む)が発生しないようになります。行。
ただし、このテーマをより深く理解するには、をお勧めしますビル・ピューの記事。そしてそれを決して試みないでください:)
他のヒント
この記事では、5.0以前のJavaメモリモデル(JMM)について言及しています。そのモデルでは、同期ブロックを残して、メインメモリへの書き込みを強制しました。そのため、Singletonオブジェクトが参照される前にプッシュされることを確認する試みのようです。ただし、インスタンスへの書き込みはブロック(ローチモーテル)に移動できるため、まったく機能しません。
ただし、5.0以前のモデルは正しく実装されていませんでした。 1.4は5.0モデルに従う必要があります。クラスは遅延して初期化されるため、次のように記述することもできます
public static final Singleton instance = new Singleton();
より良いことに、シングルトンは悪であるため使用しないでください。
Jon Skeetは正しい: Bill Pughのを読む記事。 Hansが使用するイディオムは、機能しない正確な形式であり、使用すべきではありません。
これは安全ではありません:
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
これも安全ではありません:
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}
これらのいずれも実行しないでください。
代わりに、メソッド全体を同期します:
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
このオブジェクトを1秒間に10億回取得しない限り、実際のパフォーマンスヒットは無視できます。
John Skeet の推奨事項に従う:
ただし、対象をさらに深くするために Bill Pughの記事をお勧めします。そして それを決して試みないでください:)
2番目の同期ブロックのキーは次のとおりです。
このコードは、 内部のヘルパーオブジェクト 同期ブロック。直感的なアイデア ここにメモリがあるはずです 障壁 同期がリリースされ、 の並べ替えを防ぐ必要があります ヘルパーオブジェクトの初期化 そしてフィールドへの割り当て ヘルパー。
したがって、基本的に、内部同期ブロックでは、「チート」を試みています。同期ブロック内でインスタンスを作成するJMM。同期ブロックが終了する前にJMMにその割り当てを実行させます。しかし、ここでの問題は、JMMが私たちを前に進め、syncブロック内のsyncブロックの前にある割り当てを移動し、問題をbeginnigに戻すことです。
これは私がそれらの記事から理解したことであり、本当に興味深く、また返信をありがとう。
大丈夫ですが、記事ではそう言っています
リスト7のコードは、メモリモデルの現在の定義が原因で機能しません。 Java Language Specification(JLS)は、同期ブロック内のコードを同期ブロックから移動しないことを要求しています。ただし、同期ブロックにないコードを同期ブロックに移動できないとは言っていません。
また、JVMが" pseudo-code"への次の翻訳を行うようにも見えます。 ASMの場合:
public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 //inst = new Singleton(); //4 instance = new Singleton(); } //instance = inst; //5 } } } return instance; }
これまでのところ、" instance = inst"の後の書き込み禁止のポイント達成されていない?
リンクのおかげで、今記事を読みます。
Java 5以降、フィールドをvolatileに宣言することにより、ダブルチェックロックを機能させることができます。
http://www.cs.umd.eduを参照完全な説明については、/〜pugh / java / memoryModel / DoubleCheckedLocking.html を参照してください。
このイディオムに関して、非常に賢明で明確な記事があります:
http:// www .javaworld.com / javaworld / jw-02-2001 / jw-0209-double.html?page = 1
一方で、dhighwayman.myopenidの意味は、ライターが同じクラスを参照する1つの同期ブロック(synchronized(Singleton.class))を同じクラスを参照する別の同期ブロック内に配置した理由だと思います。そのブロック内に新しいインスタンス(Singleton inst = instance;)が作成されると発生する可能性があり、スレッドセーフであることを保証するには、別の同期を記述する必要があります。
それ以外の場合、私は感覚を見ることができません。
本当に素晴らしい紹介については、 JavaメモリモデルでGoogle Tech Talkをご覧くださいJMMの細かい点まで。ここにはないため、Jeremy Mansonsのブログ 'Java Concurrency' を指摘したいと思います。 ダブルチェックロック(誰でも誰でもJavaの世界にはこれに関する記事があるようです:)。
Java 5以降では、実際にアクセサ全体を同期するよりも優れたダブルチェックのバリアントがあります。これは、ダブルチェックロック宣言にも記載されています。 :
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}
ここでの主な違いは、変数宣言で volatile を使用していることです。それ以外の場合は動作せず、とにかくJava 1.4以下では動作しません。