誰もがシングルトンモデルに精通しており、彼らは皆、それが怠zyで、空腹などである理由を知っています。しかし、あなたはシングルトンパターンを完全に理解していますか?今日は、私の目にシングルトンを見るためにあなたを連れて行きます。それはあなたの理解とは異なるかもしれません。
ここに簡単な例があります:
// Simple Lazy Public Class Singleton {// Singleton Instance変数プライベート静的Singleton Instance = null; //私的建設方法コンストラクターPrivate Singleton(){}を介して外部クラスをインスタンス化できないことを確認するため。 } system.out.println( "私はシンプルな怠zyなシングルトンです!");インスタンスを返す; }}マルチスレッドの場合、上記のコードが安全でないことがわかります。 2つのスレッドがif(instance == null)を入力すると、両方のスレッドがインスタンスが空であると判断し、2つのインスタンスが取得されます。これは私たちが望むシングルトンではありません。
次に、ロックを使用して相互排除を実現し、シングルトンの実装を確保します。
//同期メソッドレイジーパブリッククラスシングルトン{//シングルトンインスタンス変数プライベート静的シングルトンインスタンス= null; //私的建設方法コンストラクターを介して外部クラスをインスタンス化できないことを確認するためのPrivate Singleton(){} } system.out.println( "私は同期方法怠singleton!");インスタンスを返す; }}同期を追加すると、スレッドの安全性が確保されますが、これは最良の方法ですか?このようにして、getInstance()メソッドを呼び出すたびにロックされ、getInstance()を初めて呼び出すときにロックする必要があるため、明らかにそうではありません。これは明らかに私たちのプログラムのパフォーマンスに影響します。私たちはより良い方法を見つけ続けています。
分析後、インスタンス= new Singleton()がスレッド相互除外であることを確認することによってのみ、スレッドの安全性を確保できるため、次のバージョンを使用できます。
//ダブルロック怠zyなパブリッククラスシングルトン{//シングルトンインスタンス変数プライベート静的シングルトンインスタンス= null; //プライベート建設方法コンストラクターを介して外部クラスをインスタンス化できないことを確認するためのプライベートシングルトン(){} }}} system.out.println( "私はダブルロック怠zyなシングルトン!");インスタンスを返す; }}今回は、スレッドの安全性の問題を解決するだけでなく、getInstance()を呼び出すたびにロックを追加しないようにするため、パフォーマンスの低下になります。それは完璧な解決策のように見えます、それは実際にそうですか?
残念ながら、事実は私たちが思ったほど完璧ではありません。 Java Platform Memory Modelには、「Out-Obsor-Of-Order Write」と呼ばれるメカニズムがあります。ダブルチェックロック方法の障害を引き起こすのは、このメカニズムです。この問題の鍵は、上記のコードの5行目にあります:instance = new Singleton();この行は実際に2つのことを行います。1。コンストラクターを呼び出してインスタンスを作成します。 2.このインスタンスをインスタンス変数インスタンスに割り当てます。しかし、問題は、JVMのこれら2つのステップが順序を保証しないことです。つまり、インスタンスは、コンストラクターを呼び出す前に空ではないように設定されている可能性があります。一緒に分析しましょう:
2つのスレッドAとbがあるとします
1。スレッドaはgetInstance()メソッドに入ります。
2。この時点でインスタンスが空になっているため、スレッドAは同期されたブロックに入ります。
3。スレッドAはインスタンス= new Singleton()を実行します。インスタンス変数インスタンスを空ではないように設定します。 (コンストラクターに電話する前のことに注意してください。)
4.スレッドA出口、スレッドBが入ります。
5。スレッドBは、インスタンスが空であるかどうかをチェックし、現時点では空ではないかどうかを確認します(3番目のステップでは、スレッドaによって空ではないように設定されています)。スレッドBは、インスタンスへの参照を返します。 (問題が発生します。現時点では、インスタンスへの参照は、コンストラクターが呼び出されないため、シングルトンインスタンスではありません。)
6.スレッドB出口とスレッドAが入ります。
7.スレッドAは、コンストラクターメソッドを呼び出し続け、インスタンスの初期化を完了し、戻ります。
良い方法はありませんか?良い方法がなければなりません、探求し続けましょう!
//順序付けられていない執筆の問題を解決する怠zyなパブリッククラスのシングルトン{// singleton instance可変プライベート静的シングルトンインスタンス= null; //私的建設方法コンストラクターを介して外部クラスをインスタンス化できないことを確認するためのprivate singleton(){} // 2 if(temp == null){synchronized(singleton.class){// 3 temp = new Singleton(); // 4} instance = temp; // 5}}} system.out.println( "怠zyなシングルトンの秩序のない文章を解決しています!");インスタンスを返す; }} 1。スレッドaはgetInstance()メソッドに入ります。
2。インスタンスが空であるため、スレッドAは位置の最初の同期ブロックに入ります// 1。
3.スレッドAは// 2でコードを実行し、ローカル変数温度にインスタンスを割り当てます。インスタンスは空であるため、温度も空です。
4.温度が空であるため、スレッドAは位置// 3の2番目の同期ブロックに入ります。 (このロックは少し冗長だと思った)
5.スレッドAは、ポジション// 4でコードを実行します。 (「執筆の執筆」という問題)
6.スレッドAブロックがある場合、スレッドBはgetInstance()メソッドに入ります。
7.インスタンスが空であるため、スレッドBは最初の同期ブロックに入ろうとします。しかし、スレッドAはすでに内部にあるためです。そのため、入ることは不可能です。スレッドBブロック。
8。スレッドAがアクティブになり、位置// 4のコードを実行し続けます。コンストラクターに電話してください。インスタンスを生成します。
9. TEMPのインスタンス参照をインスタンスに割り当てます。 2つの同期ブロックを終了します。インスタンスを返します。
10。スレッドBがアクティブになり、最初の同期ブロックに入ります。
11.スレッドBは、ポジション// 2でコードを実行し、インスタンスインスタンスをTEMPローカル変数に割り当てます。
12。スレッドBは、ローカル変数の温度が空ではないと判断するため、Ifブロックをスキップします。インスタンスインスタンスを返します。
これまでのところ、上記の問題を解決しましたが、スレッドの安全性の問題を解決するために、体に巻き付けられた糸がたくさんあるように感じることが突然わかりました...それは乱雑ですので、合理化する必要があります:
// Hungry Public Class Singleton {// Singleton Variable、Staticは、クラスがロードされたときに1回初期化され、スレッドの安全性のStatic Singleton Instance = new Singleton(); //外部クラスをコンストラクターを介してインスタンス化できないことを確認するためのプライベート建設方法。 private singleton(){} // singletonオブジェクトインスタンスを取得しますpublic static singleton getInstance(){system.out.println( "私は空腹のシングルトン!");インスタンスを返す; }}上記のコードを見たとき、私はすぐに世界が静かだと感じました。ただし、この方法では、飢えた男スタイルの方法を採用しています。これの1つの欠点は、建設のシングルトンが大きく、建設が完了した後に使用されない場合、リソースの無駄につながることです。
完璧な方法はありますか?見続ける:
//インナークラスを実装している怠zyなパブリッククラスのシングルトン{プライベート静的クラスSingletonHolder {// Singleton Variable Private Static Singleton Instance = new Singleton(); } //外部クラスをコンストラクターを介してインスタンス化できないことを確認するためのプライベート建設方法。 private singleton(){} // get singleton object instance public static singleton getInstance(){system.out.println( "私は内部クラスのシングルトン!"); singletonholder.instanceを返します。 }}怠zy(上記のリソースの無駄を避けます)、スレッドセーフ、およびシンプルコード。 Javaメカニズムは、getInstance()メソッドが初めて(怠zyの実装)と呼ばれ、その読み込みプロセスがスレッドセーフ(スレッドセーフの実装)である場合にのみ、内部クラスのシングルトンホルダーがロードされることを規定しているためです。インスタンスは、内部クラスがロードされるとインスタンス化されます。
上記の秩序のない文章について簡単に話しましょう。これがJVMの特性です。たとえば、2つの変数、文字列Aを宣言します。文字列B; JVMは最初のまたはbをロードできます。同様に、instance = new Singleton(); Singletonのコンストラクターを呼び出す前に、インスタンスを非空白に設定することができます。これは、シングルトンのオブジェクトがインスタンス化されていないと言っている多くの人々にとって質問です。今の価値は何ですか?この問題を理解したい場合は、文がどのようにinstance = new Singleton()を理解するかを理解する必要があります。実行されます。これがあなたにそれを説明するための擬似コードです:
mem = allocate(); //シングルトンオブジェクトのメモリを割り当てます。 instance = mem; //インスタンスは現在空ではないが、まだ初期化されていないことに注意してください。 Ctorsingleton(インスタンス); // Singletonのコンストラクターを呼び出し、Passインスタンス。
スレッドがinstance = mem;、インスタンスが空でないことを実行すると、確認できます。別のスレッドがプログラムと審査員のインスタンスに空白がないと入力した場合、インスタンスを返すためにジャンプします。そしてこの時点で、シングルトンのコンストラクターはインスタンスと呼ばれておらず、現在の値はallocate()によって返されるメモリオブジェクトです。したがって、2番目のスレッドはシングルトンオブジェクトではなく、メモリオブジェクトを取得します。
上記は、シングルトンモデルの私の小さな考えと理解です。私はすべての偉大な神々が来て導き、批判することを温かく歓迎します。