web-dev-qa-db-ja.com

シングルトンのダブルチェックロック

シングルトンパターンのカスタムクラスを次に示します。このコードでは、次のようにダブルチェックロックを使用します。いくつかのソースで多くの投稿を読んでいるので、2つの並行スレッドが同時に実行されて2つの異なるオブジェクトが作成されるのを防ぐため、ダブルチェックが役立つと言われています。

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

私はまだ上記のコードをあまり理解していません。インスタンスがnullのときに2つのスレッドが一緒に同じコード行を実行する場合、問題は何ですか?

if (searchBox == null) {
                synchronized (SearchBox.class) {
                    if (searchBox == null) {
                        searchBox = new SearchBox();
                    }
                }
            }

それが現れたら。 2つのスレッドは両方ともオブジェクトがnullであると認識します。その後、両方が同期します。そして、それらは再びチェックし、それでもnullを見る。 2つの異なるオブジェクトを作成します。 OOOPS。

説明してください。私は間違って何を理解していますか?

ありがとう:)

58
hqt

いいえ、SearchBox.classのロックを取得しているため、一度に1つのスレッドのみが同期ブロックに入力します。そのため、最初のスレッドがsearchBoxがnullであると検出して作成し、同期ブロックを離れます。次に、2番目のスレッドがブロックに入り、最初のスレッドが既に作成されているためsearchBoxがnullではないことがわかりますsearchBoxの新しいインスタンスは作成されません。

コードが実行されるたびにロックが取得されるのを避けるために、二重チェックパターンが使用されます。呼び出しが一緒に発生していない場合、最初の条件は失敗し、コード実行はロックを実行しないため、リソースが節約されます。

65
Arun P Johny

このコードを見てみましょう:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

これについて推論してみましょう。 2つのスレッドABがあり、そのうちの少なくとも1つが行3に到達し、searchBox == nulltrueであると仮定するとしましょう。 synchronizedブロックのため、2行のスレッドを同時に両方とも行3に置くことはできません。これは、二重チェックロックが機能する理由を理解するためのkeyです。そのため、AまたはBのいずれかが最初にsynchronizedを介して作成された必要があります。一般性を失うことなく、そのスレッドはAであると言います。次に、searchBox == nullがtrueになると、ステートメントの本体に入り、searchBoxSearchBoxの新しいインスタンスに設定します。その後、最終的にsynchronizedブロックを終了します。 Bが入る番です。Bが終了するのを待って、Aがブロックされたことを思い出してください。ブロックに入ると、searchBoxを観察します。ただし、AsearchBoxnull以外の値に設定したままになります。できた.

ところで、Javaでシングルトンを実装する最良の方法は、単一要素のenum型を使用することです。 有効なJava から:

このアプローチはまだ広く採用されていませんが、シングルトンを実装するには、単一要素の列挙型が最善の方法です。

25
jason

このダブルチェックロックは、シングルトンを同時に呼び出す多くのスレッド、または一般にロックを取得するコストが心配な場合にのみ必要です。

その目的は、不必要な同期を防ぎ、マルチスレッド環境でコードを高速に保つことです。

詳細については、このリンクをご覧ください。

Java 1.5以上で実行している場合、ダブルチェックロックメカニズムでvolatileキーワードを使用すると、正常に機能します。volatileキーワード、あなたの例は上記の同じリンクに従って壊れていません。

10
if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. インスタンスがすでに作成されている場合は、何もしないでください-スレッドのロックを避けてください
  2. ロックを取得した最初のスレッドは、そのようなオブジェクトが存在しないことを確認して作成します。ロックを解除し、2番目のロックも同じことができます。最初のロックがオブジェクトを作成した可能性があるため、オブジェクトが存在するかどうかを確認する必要があります。

したがって、基本的に外側のifは冗長ロックを防ぐために使用されます。これにより、すべてのスレッドにオブジェクトがすでに存在し、ロック/実行の必要がないことが通知されます。また、内側のifは、別のスレッドが既にオブジェクトを作成したかどうかを並行スレッドに知らせるために使用されます。

2
stan0