2

Double-checked locking: Clever, but breakedなどのさまざまな記事を読み、次のコードがマルチスレッドの使用で壊れている理由を理解しました。

class SomeClass {
  private Resource resource = null;
  public Resource getResource() {
    if (resource == null) {
      synchronized {
        if (resource == null) 
          resource = new Resource();
      }
    }
    return resource;
  }
}

ただし、その説明によると、スレッドが同期ブロックを終了すると、書き込みバリアが実行されます。ロックを解放する前に、そのブロックで変更されたすべての変数をメイン メモリにフラッシュする必要があります。したがって、スレッド A が同期ブロックに実行されると、次のプロセスが順番に実行されます。

  1. 新しい Resource オブジェクトのメモリが割り当てられます。
  2. Resource のコンストラクタが呼び出され、
  3. 新しいオブジェクトのメンバー フィールドを初期化します。
  4. SomeClass のフィールド リソースには、新しく作成されたオブジェクトへの参照が割り当てられます。

最後に、スレッド A が同期ブロックから出る前に、スレッド A はそのローカル リソース オブジェクトをメイン メモリに書き戻し、スレッド B は、同期ブロックに到達すると、この新しく作成されたリソースをメイン メモリから読み取ります。

スレッド B がこれらのメモリ操作を、スレッド A が実行する順序とは異なる順序で見ることができるのはなぜですか? スレッド B は、共有可能なメイン メモリからしかリソース オブジェクトを読み取ることができないため、スレッド A が同期ブロックから出るときにローカル メモリをメイン メモリにフラッシュするまで、リソース オブジェクトが作成されたことをスレッド B は認識しないと思いましたか?

私の理解を修正してください....ありがとうございます。

4

4 に答える 4

2

引用した記事は、Java 5.0 より前の Java メモリ モデルに言及しています。

Java 5.0+ では、それが機能するためresourceに宣言する必要がありますvolatile。変更がメイン メモリにフラッシュされたとしてもvolatile、スレッド B が独自のローカル キャッシュ (値が null の場合) ではなく、メイン メモリから新しい値を読み取るという保証はありません (を除く)。

以前のバージョンでは、volatile は並べ替えに厳密な制限を課していなかったため、ダブル チェック ロックが適切に機能することが保証されていませんでした。

于 2012-08-23T07:17:52.010 に答える
1

「ダブルチェックロック」は死なないミームの一つです。列挙型を使用するIMHOははるかにスマートです(Josh BlochがEffective Java 2nd editionで提案したように)

enum SomeClass {
    INSTANCE; // thread safe and lazy loaded
}

あなたが言及しているバグは、2004 年に Java 5.0 で修正されました。

要するに、a) 使用しない b) Java 5.0+ のバージョンを使用する c) Java の非常に古いサポートされていないバージョンを使用しないでください。

于 2012-08-23T07:30:21.247 に答える
1

最後に、スレッド A が同期ブロックから出る前に、スレッド A はそのローカル リソース オブジェクトをメイン メモリに書き戻し、スレッド B は、同期ブロックに到達すると、この新しく作成されたリソースをメイン メモリから読み取ります。

これが壊れるところです。スレッド B はresource同期せずにアクセスするため、その操作に読み取りバリアはありません。したがって、resourceセルまたは (少し後で)Resourceインスタンスのフィールドに対応するセルの古いキャッシュ コピーが表示される場合があります。

Costi Ciudatu の修正は、Java バージョン >= 5.0 に対して正しいです。しかし、それよりも古いバージョンの場合、 のセマンティクスはvolatile、すべての変更が A からメイン メモリ、B にフラッシュされることを保証しませんでした。

于 2012-08-23T07:30:29.953 に答える
1

他の人がすでに行った以上のことを言うつもりはありませんが、これは非常に頻繁に使用されるパターンであるため、そのためのユーティリティ メソッドを作成してみませんか? Like:サプライヤーメモ化

于 2012-08-23T07:43:35.253 に答える