75

これがシングルトン パターンのカスタム クラスです。このコードでは、以下のようにダブルチェック ロックを使用しています。いくつかの情報源に関する多くの投稿を読んだところ、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;
        }
}

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

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

それが現れるとき。2 つのスレッドの両方で、オブジェクトが null であることがわかります。その後、両方が同期します。その後、再度チェックしても null が表示されます。2 つの異なるオブジェクトを作成します。おっと。

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

ありがとう :)

4

4 に答える 4

88

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

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

于 2013-08-07T02:46:04.983 に答える
32

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

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

これについて推論してみましょう。2 つのスレッドがAありB、そのうちの少なくとも 1 つが 3 行目に到達し、 が であると仮定しsearchBox == nullますtrue。ブロックのため、 2 つのスレッドが同時に 3 行目にあることはできません。synchronizedこれは、ダブルチェック ロックが機能する理由を理解するための鍵です。Aしたがって、どちらかBsynchronized最初に成功した場合に違いありません。一般性を失うことなく、そのスレッドはA. 次に、searchBox == nullが真であることを確認すると、ステートメントの本体に入り、searchBoxの新しいインスタンスに設定されますSearchBox。その後、最終的にsynchronizedブロックを終了します。今度は Bさんが入る番です。覚えておいてください。B終了するのを待ってブロックされましAた。ブロックに入ると、 が観察されsearchBoxます。しかし、非値にA設定したままにしておきます。終わり。searchBoxnull

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

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

于 2013-08-07T02:48:57.600 に答える
12

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

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

詳細については、このリンクを参照してください。

Java 1.5 以降で実行していてvolatile、ダブルチェック ロック メカニズムでキーワードを使用している場合、問題なく動作します。キーワードを使用しているvolatileため、上記の同じリンクによると、例は壊れていません。

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

したがって、基本的にアウターifは冗長なロックを防ぐために使用されます。これにより、すべてのスレッドに、オブジェクトが既に存在し、ロック/何もする必要がないことがわかります。また、インナーifは、別のスレッドが既にオブジェクトを作成しているかどうかを並行スレッドに知らせるために使用されます。

于 2013-08-07T11:58:34.577 に答える