シングルトンパターンのカスタムクラスを次に示します。このコードでは、次のようにダブルチェックロックを使用します。いくつかのソースで多くの投稿を読んでいるので、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。
説明してください。私は間違って何を理解していますか?
ありがとう:)
いいえ、SearchBox.class
のロックを取得しているため、一度に1つのスレッドのみが同期ブロックに入力します。そのため、最初のスレッドがsearchBox
がnullであると検出して作成し、同期ブロックを離れます。次に、2番目のスレッドがブロックに入り、最初のスレッドが既に作成されているためsearchBox
がnullではないことがわかりますsearchBox
の新しいインスタンスは作成されません。
コードが実行されるたびにロックが取得されるのを避けるために、二重チェックパターンが使用されます。呼び出しが一緒に発生していない場合、最初の条件は失敗し、コード実行はロックを実行しないため、リソースが節約されます。
このコードを見てみましょう:
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
であると仮定するとしましょう。 synchronized
ブロックのため、2行のスレッドを同時に両方とも行3に置くことはできません。これは、二重チェックロックが機能する理由を理解するためのkeyです。そのため、A
またはB
のいずれかが最初にsynchronized
を介して作成された必要があります。一般性を失うことなく、そのスレッドはA
であると言います。次に、searchBox == null
がtrueになると、ステートメントの本体に入り、searchBox
をSearchBox
の新しいインスタンスに設定します。その後、最終的にsynchronized
ブロックを終了します。 B
が入る番です。B
が終了するのを待って、A
がブロックされたことを思い出してください。ブロックに入ると、searchBox
を観察します。ただし、A
はsearchBox
をnull
以外の値に設定したままになります。できた.
ところで、Javaでシングルトンを実装する最良の方法は、単一要素のenum
型を使用することです。 有効なJava から:
このアプローチはまだ広く採用されていませんが、シングルトンを実装するには、単一要素の列挙型が最善の方法です。
このダブルチェックロックは、シングルトンを同時に呼び出す多くのスレッド、または一般にロックを取得するコストが心配な場合にのみ必要です。
その目的は、不必要な同期を防ぎ、マルチスレッド環境でコードを高速に保つことです。
Java 1.5以上で実行している場合、ダブルチェックロックメカニズムでvolatile
キーワードを使用すると、正常に機能します。volatile
キーワード、あなたの例は上記の同じリンクに従って壊れていません。
if (searchBox == null) { //1
synchronized (SearchBox.class) {
if (searchBox == null) { //2
searchBox = new SearchBox();
}
}
}
}
したがって、基本的に外側のif
は冗長ロックを防ぐために使用されます。これにより、すべてのスレッドにオブジェクトがすでに存在し、ロック/実行の必要がないことが通知されます。また、内側のif
は、別のスレッドが既にオブジェクトを作成したかどうかを並行スレッドに知らせるために使用されます。