1。シングルトンモードとは何ですか
Singletonパターンとは、アプリケーションの生涯を通じて1つのインスタンスのみが存在することを指します。シングルトンパターンは、広く使用されているデザインパターンです。インスタンスオブジェクトの作成を避け、インスタンスの作成のシステムオーバーヘッドを減らし、メモリを保存することができる多くの利点があります。
シングルトンモードには3つの要件があります。
2。シングルトンパターンと静的クラスの違い
まず、静的クラスとは何かを理解しましょう。静的クラスとは、クラスに静的メソッドと静的フィールドがあることを意味します。コンストラクターはプライベートによって変更されるため、インスタンス化することはできません。数学クラスは静的クラスです。
静的クラスが何であるかを知った後、それらの違いについて話しましょう。
1)まず、シングルトンパターンは、グローバルにユニークなオブジェクトを提供します。静的クラスは、多くの静的方法のみを提供します。これらの方法は作成する必要はなく、クラスを通じて直接呼び出すことができます。
2)Singletonパターンの柔軟性が高く、静的クラスはすべて静的な方法であるため、メソッドをオーバーライドできます。
3)非常に重いオブジェクトの場合、シングルトンのパターンはロードが怠けている可能性がありますが、静的クラスはそれを行うことはできません。
次に、静的クラスを使用する必要があります。シングルトンモードをいつ使用する必要がありますか?まず第一に、いくつかのツールメソッドを使用したい場合は、静的クラスを使用することをお勧めします。静的な類推は、コンピレーション期間中に静的バインディングが実行されるため、シングルトンクラスよりも高速です。ステータス情報またはアクセスリソースを維持する場合は、Singletonモードを使用する必要があります。また、オブジェクト指向の機能(継承、多型など)が必要な場合は、Singletonクラスを選択し、いくつかの方法のみを提供する場合は、静的クラスを選択すると言えます。
3.シングルトンモードの実装方法
1。空腹のマンモード
いわゆる空腹モードは、すぐにロードすることです。一般に、インスタンスはGetInstanceFメソッドを呼び出す前に生成されました。つまり、クラスがロードされたときに生成されたことを意味します。このモデルの欠点は非常に明白です。つまり、リソースを占めることです。 Singletonクラスが大きい場合、実際に使用してからインスタンスを生成したいと考えています。したがって、この方法は、より少ないリソースを占めるクラスに適しており、初期化中に使用されます。
クラスSingletonhungary {private static singletonhungary singletonhungary = new singletonhungary(); //コンストラクターをプライベートに設定して、新しいプライベートSingletonHungary(){} public static singletonhungary getInstance(){return singletonhungary; }}2。レイジーモード
怠zyモードは怠zyなロードで、怠zyなロードとも呼ばれます。メモリが無駄にならないように、プログラムを使用する必要があるときにインスタンスを作成します。レイジーモードの場合、5つの実装方法を次に示します。いくつかの実装方法はスレッドが安全であるため、マルチスレッドの並行性環境でリソース同期の問題が発生する可能性があります。
まず第一に、最初の方法は、単一のスレッドに問題はないが、マルチスレッドに問題があることです。
// Singleton Mode 1-Thread Unsafe Class SingletonLazy1の怠zyな実装{private static singletonlazy1 singletonlazy; private singletonlazy1(){} public static singletonlazy1 getInstance(){if(null == singletonlazy){try {//オブジェクトスレッドを作成する前に準備をシミュレートします。 } catch(arturnedexception e){e.printstacktrace(); } singletonlazy = new singletonlazy1(); } singletonlazyを返します。 }}テストするために10個の非同期スレッドをシミュレートしましょう。
public class singletonlazytest {public static void main(string [] args){thread2 [] threadarr = new shood2 [10]; for(int i = 0; i <threadarr.length; i ++){threadarr [i] = new shood2(); threadarr [i] .start(); }}} //スレッドクラススレッド2を拡張スレッド{@Override public void run(){system.out.println(singletonlazy1.getInstance()。hashcode()); }}実行結果:
124191239
124191239
872096466
1603289047
1698032342
1913667618
371739364
124191239
1723650563
367137303
ハッシュコードがすべて同じではないことがわかります。つまり、シングルトンパターンの要件を満たしていないマルチスレッド環境で複数のオブジェクトが生成されます。
では、スレッドを安全にする方法は? 2番目の方法では、同期されたキーワードを使用して、GetInstanceメソッドを同期します。
// singletonモードレイジー実装2--スレッド安全//同期メソッドを設定することにより、効率が低すぎると、メソッド全体がロックされています。 private singletonlazy2(){} public static synchronized singletonlazy2 getInstance(){try {if(null == singletonlazy){//オブジェクトスレッドを作成する前に準備をシミュレートします。 singletonlazy = new singletonlazy2(); }} catch(arturnedexception e){e.printstacktrace(); } singletonlazyを返します。 }}上記のテストクラスを使用して、テスト結果:
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
ご覧のとおり、この方法はスレッドの安全性を実現します。ただし、不利な点は、効率が低すぎて同期して実行されることです。次のスレッドがオブジェクトを取得したい場合、実行を続ける前に前のスレッドがリリースされるのを待つ必要があります。
その後、メソッドをロックすることはできませんが、コードを内部にロックします。これにより、スレッドの安全性も実現できます。しかし、この方法は同期法と同じであり、同期して実行され、効率が非常に低くなります。
// singletonlazy実装3--スレッド安全//同期コードブロックを設定することにより、効率が低すぎ、コードブロック全体がクラスSingletonlazy3 {private static singletonlazy3 singletonlazy; private singletonlazy3(){} public static singletonlazy3 getInstance(){try {synchronized(singletonlazy3.class){if(null == singletonlazy){//オブジェクトスレッドを作成する前に準備をするためにシミュレートする(1000); singletonlazy = new singletonlazy3(); }}} catch(arturnedexception e){// todo:handle exception} return singletonlazy; }}コードを最適化し続けましょう。オブジェクトを作成するコードのみをロックしますが、これによりスレッドの安全性を確保できますか?
//シングルトンモードの怠zyな実装4--スレッドUnsafe //インスタンスを作成するコードのみが同期コードブロックを設定することによって同期されます//しかし、スレッド安全性の問題class singletonlazy4 {private static singletonlazy4 singletonlazy; private singletonlazy4(){} public static singletonlazy4 getInstance(){try {if(null == singletonlazy){// code 1 //オブジェクトスレッドを作成する前に準備をするようにシミュレートする(1000); Synchronized(Singletonlazy4.class){singletonlazy = new singletonlazy4(); //コード2}}} catch(arternedexception e){// todo:handle exception} singletonlazyを返します。 }}実行中の結果を見てみましょう。
1210004989
1425839054
1723650563
389001266
1356914048
389001266
1560241484
278778395
124191239
367137303
結果から判断すると、この方法はスレッドの安全性を保証することはできません。なぜ? 2つのスレッドAとBが同時に「コード1」に移動すると仮定します。これは、この時点でオブジェクトがまだ空であるため、両方がメソッドを入力できるためです。最初にロックをつかみ、オブジェクトを作成します。スレッドBがロックを取得すると、リリースされると「コード2」にも移動し、オブジェクトが作成されます。したがって、マルチスレッド環境ではシングルトンを保証することはできません。
引き続き最適化しましょう。上記の方法に問題があるため、同期コードブロックでヌルの判断を下すことができます。この方法は、DCLダブルチェックロックメカニズムです。
//シングルトンモードのスラージーマンは5スレッドセーフティを実装しますSingletonlazy5オブジェクトが作成されると、Singletonlazy5オブジェクトが取得されると、同期コードブロックと後続のコードのロックを検証する必要はありません。クラスSingletonLazy5 {private static volatile singletonlazy5 singletonlazy; private singletonlazy5(){} public static singletonlazy5 getInstance(){try {if(null == singletonlazy){//オブジェクトスレッドを作成する前に準備をシミュレートします。 Synchronized(singletonlazy5.class){if(null == singletonlazy){singletonlazy = new singletonlazy5(); }}}} catch(arternedexception e){} return singletonlazy; }}実行結果:
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
DCLのダブルチェックロックメカニズムは、怠zyなロードシングルトンモードの効率とスレッドの安全性の問題を解決することがわかります。これは、最も頻繁に使用する方法でもあります。
揮発性キーワード
ここでは、Singletonlazyを定義するとき、揮発性キーワードが使用されていることに気付きました。これは、指示が並べ替えるのを防ぐためです。なぜこれを行う必要があるのですか?シナリオを見てみましょう。
コードはsingletonlazy = new singletonlazy5()に移動します。それは文のようですが、これは原子操作ではありません(すべてが実行されるか、すべてが実行されず、半分は実行できません)。この文は8つのアセンブリの指示にまとめられており、約3つのことが行われます。
1。singletonlazy5インスタンスにメモリを割り当てます。
2。singletonlazy5コンストラクターを初期化します
3。SingleTonlazyオブジェクトを割り当てられたメモリ空間に向けます(このインスタンスはnullではないことに注意してください)。
Javaコンパイラは、プロセッサがオーダー(オーダー)を実行することを許可し、キャッシュの順序はJDK1.5の前にJMM(Javaメモリメデル)のメインメモリワトバックに登録するため、上記の2番目と3番目のポイントの順序は保証できません。つまり、実行命令は1-2-3または1-3-2になる場合があります。後者であり、3が実行され、2が実行されない場合は、スレッド2に切り替えられます。この時点で、Singletonlazyはすでにスレッド1で3番目のポイントを実行しています。Singletonlazyはすでに非空白です。さらに、追跡が難しく、再現が困難なこの種のエラーは、デバッグの最後の週には見当たりません。
シングルトンを実装するDCLの執筆方法は、多くの技術書や教科書(JDK1.4の以前のバージョンに基づく本を含む)で推奨されていますが、実際には完全に正しいわけではありません。実際、DCLは、2つと3つのステップの順序を保証できるかどうかに応じて、一部の言語(Cなど)で実行可能です。 JDK1.5の後、役人はこの問題に気づいたため、JMMが調整され、揮発性キーワードが具体化されました。したがって、JDKが1.5以降のバージョンである場合、揮発性キーワードをSingletonlazyの定義に追加するだけで、Singletonlazyがメインメモリから毎回読み取られ、DCLライティング方法を使用してSingletonモードを完了することができます。もちろん、揮発性は多かれ少なかれパフォーマンスに影響を与えます。最も重要なことは、JDK1.42および以前のバージョンを考慮する必要があることです。そのため、シングルトンパターンライティングの改善は依然として継続しています。
3。静的内部クラス
上記の考慮事項に基づいて、静的インナークラスを使用してSingletonパターンを実装できます。コードは次のとおりです。
//静的な内部クラスを使用してシングルトンモードを実装します。 } public static singletonstaticinner getInstance(){try {thread.sleep(1000); } catch(arturnedexception e){// todo auto-fenated catch block e.printstacktrace(); } singletoninner.singletonstaticinnerを返します。 }}このように同期操作を明示的に実行しないことがわかります。 Hungry Manモードと同様に、JVMがクラスの静的メンバーを1回しか読み込めないことを保証する機能であり、JVMレベルから1つのインスタンスオブジェクトしか存在しないようにします。問題は、この方法と空腹の男モデルの違いは何ですか?すぐに読み込まれませんか?実際、クラスがロードされると、その内側のクラスは同時にロードされません。クラスはロードされます。これは、静的メンバーの1人(静的ドメイン、コンストラクター、静的メソッドなど)が呼び出された場合にのみ発生します。
この方法は、シングルトンパターンを実装するための最適なソリューションであると言えます。
4。静的コードブロック
静的コードブロックの実装シングルトンパターンです。この方法は最初の方法に似ており、空腹の男モデルでもあります。
//静的コードブロックを使用して、Singleton Mode Class SingleTonStaticBlockを実装する{プライベートSingleTonStaticBlock SingleTonStaticBlock; static {singletonStaticBlock = new SingleTonStaticBlock(); } public static singletonstaticblock getInstance(){return singletonstaticblock; }}5。シリアル化と脱派化
なぜLZはシリアル化と降下を推奨するのですか? Singletonモードはスレッドの安全性を確保できるため、シリアル化と降下の場合には複数のオブジェクトが生成されます。次のテストクラスを実行し、
public class singletonstaticinnerserializetest {public static void main(string [] args){try {singletonstaticinnerserialize serialize = singletonstaticinnerserialize.getInstance(); system.out.println(serialize.hashcode()); // serialize fileoutputStream fo = new fileoutputStream( "tem"); ObjectOutputStream OO = new objectOutputStream(fo); oo.writeobject(serialize); oo.close(); fo.close(); // deserialize fileInputStream fi = new fileInputStream( "tem"); ObjectInputStream oi = new ObjectInputStream(fi); singletonstaticinnerserialize serialize2 =(singletonstaticinnerserialize)oi.readobject(); oi.close(); fi.close(); system.out.println(serialize2.hashcode()); } catch(Exception e){e.printstacktrace(); }}} //匿名の内部クラスを使用して、Singletonパターンを実装します。シリアル化と降下化に遭遇した場合、同じインスタンスが取得されません。//Solveこの問題は、シリアル化中にReadResolveメソッドを使用すること、つまりコメントの一部を削除することです。クラスSingleTonStaticInnerSerializeは、Serializable { / *** 2018年3月28日* / private static final long serialversionuid = 1l; Private static class InnerClass {private static singletonstaticinnerserialize singletonstaticinnerserialize = new singletonstaticinnerserialize(); } public static singletonstaticinnerserialize getInstance(){return inernalclass.singletonstaticinnerserialize; } //保護されたオブジェクトreadResolve(){// System.out.println( "readResolveメソッドは呼び出されました。ご覧のとおり:
865113938
1078694789
結果は、シングルトンパターンに違反するのは実際に2つの異なるオブジェクトインスタンスであることを示しています。では、この問題を解決する方法は?解決策は、DeserializationでReadResolve()メソッドを使用し、上記のコメントコードを削除し、再度実行することです。
865113938
ReadResolveメソッドが呼び出されました
865113938
問題は、ReadResolve()メソッドの神聖な方法は誰ですか?実際、JVMがメモリから新しいオブジェクトを脱必要にして「組み立てる」と、このReadResolveメソッドを自動的に呼び出して指定したオブジェクトを返し、Singletonルールが保証されます。 ReadResolve()の出現により、プログラマーは、脱isionによって得られたオブジェクトを独自に制御できます。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。