I.はじめに
Singletonパターンは比較的単純で23個のデザインパターンである必要があり、それは注目のオブジェクトを作成するには、デザインパターンの種類を作成するために属します。
第二に、概念
シングルトンは、23「ギャング4分の」デザインパターンの一つであり、それは、定期的な設計上の問題を解決する方法について説明し、柔軟で再利用可能なオブジェクト指向ソフトウェア、オブジェクト、変更、テストと再利用の実現を設計するために、より便利に。
Singletonパターンは、次の問題を解決します。
-
クラスのインスタンスを1つだけ確保するには?
- どのように簡単にクラスの一意のインスタンスにアクセスしますか?
-
クラスのインスタンス化を制御する方法は?
-
クラスのインスタンス数を制限するには?
シングルトンは、上記の問題を解決する方法ですか?
- クラスの後ろのコンストラクタ。
- クラス返さパブリック静的操作の固有のインスタンスを定義します。
こののキーポイントは、デザインパターンのクラスは、独自のインスタンス化を制御することです。
クラスは、外部からインスタンス化できないことを確実にするために、クラスのコンストラクタ(プライベートコンストラクタを定義した)の背後にあります。
静的関数を使用して簡単にクラスのインスタンスにアクセスする(Singleton.getInstance())。
第三に、達成するために
1、例怠惰な単一
1 使用System.Threadingと、 2 。3 パブリック クラスSingletonTest 4。 { 5。 プライベート 静的 = SingletonTestインスタンスNULL ; 6 。7 /// <まとめ> 8 /// 隠れクラスコンストラクタ(定義されたプライベートコンストラクタ)外部からのクラスができないことを保証するためにインスタンス化された 9。 /// </要約> 10 プライベートSingletonTest() 11。 { 12は Console.WriteLineを(" ******シングルトンクラスがインスタンス化される****** " ); 13 } 14 15 // / <まとめ> 16 /// 静的クラスのインスタンス簡単にアクセス機能を使用して 17 /// </要約> 18である /// <返品> <= Crefを参照"SingletonTest" /> </戻り> 。19 公共 静的でGetInstance()SingletonTest 20であります { 21は IF(例えば== NULL ) 22は 、{ 23が インスタンス= 新しい新しいSingletonTest(); 24 } 25 26がある リターンインスタンス; 27 } 28 29 公共 ボイドPrintSomething() 30 { 31 Console.WriteLineを($ "当前线程イド为{Thread.CurrentThread.ManagedThreadId} " )。 32 Console.WriteLineを(" Singletonパターン試験" )。 33 } 34 }
通常のシングルスレッド動作の場合のコードは、単一の実施形態の定義を満たします。
しかし、マルチスレッド状況?
私たちは、テストするには、次のコードを使用します
1 // 多线程情况 2 のための(int型 i = 0 ; iは< 10 ; iは++ ) 3 { 4 Task.Run( 5 ()=> 6 { 7 SingletonTest singleton1 = SingletonTest.GetInstance(); 8 singleton1.PrintSomething() ; 9 })。 10 }
結果は以下の通りであります
予想通り、SingletonTestクラスは、この問題を解決する方法を、次に、シングルトンの要件を満たしていない、何度もインスタンス化されますか?
それは複数のスレッドによって引き起こされる問題であるので、スレッドの同期を使用する必要があります。私たちは、ロックの使用を実装するためにここにいます。
1 使用してシステムを、 2 使用System.Threadingと、 3 。4 パブリック クラスSingletonTest 。5 { 6 プライベート 静的 SingletonTestインスタンス= NULL ; 7 。8 プライベート静的オブジェクトlockObject =新しい新しいオブジェクト(); 9 10 /// <まとめ> 11 /// 隠しますクラスは、外部からインスタンス化することができないことを保証するクラス(定義プライベートコンストラクタ)のコンストラクタ 12は、 /// </要約> 13である プライベートSingletonTest() 14 { 15 のThread.sleep(500 )。 16 Console.WriteLineを(" ******シングルトンクラスがインスタンス化される****** " ); 17 } 18である 。19 /// <まとめ> 20である /// 静的クラスのインスタンスを使用して、容易に機能アクセス 21 / // </要約> 22である /// <戻り値> </戻り> <= Crefを"SingletonTest" /参照>が 23である パブリック 静的でGetInstance(SingletonTest) 24 { 25 // ダブルチェックシングルモードの実施形態を達成するための手段をロック 26がある 場合には( == NULLインスタンス) 27 { 28 ロック(lockObject) 29 { 30 IF(例えば== NULL) 31 { 32 インスタンス=新しいSingletonTest()。 33 } 34 } 35 } 36 37 戻りインスタンス。 38 } 39 40 公共 ボイドPrintSomething() 41 { 42 Console.WriteLineを($ " 当前线程イド为{Thread.CurrentThread.ManagedThreadId} " )。 43 Console.WriteLineを(" Singletonパターン試験" )。 44 } 45 }
コードは、インスタンスを作成する二つの例が空であるかどうかをチェックする前に、それが必要であるかどうかを最初の文は、(上記のコード行26)を空にしますか?なぜ?私たちは、クラスがインスタンスを初期化されている場合には、このようなシナリオを想像し、私はロックを取得する必要がありますか?回答が必要なので、最初の決意条件(上記のコード、行26)の添加されません。
再びテストコードを実行し、次のように、結果は以下のとおりです。
結果から、クラスは、マルチスレッド下クラス複数回の問題がインスタンス化さを解決するために、一度だけインスタンス化されています。しかし、この命令は、影響力の並べ替えか?volatileキーワードかどうか?それに答えるためにようこそ首長。
図2に示すように、単一の実施例を飢え(推奨)
少し複雑最初の方法は、あなたは単にそれを指し示すことができますか?次のように
1 using System; 2 using System.Threading; 3 4 public class SingletonTest2 5 { 6 // 静态变量的方式实现单例模式 7 private static readonly SingletonTest2 Instance = new SingletonTest2(); 8 9 private SingletonTest2() 10 { 11 Thread.Sleep(1000); 12 Console.WriteLine("******单例类被实例化******"); 13 } 14 15 public static SingletonTest2 GetInstance() 16 { 17 return Instance; 18 } 19 20 public void PrintSomething() 21 { 22 Console.WriteLine($"当前线程Id为{Thread.CurrentThread.ManagedThreadId}"); 23 Console.WriteLine("Singleton Pattern Test"); 24 } 25 }
这种实现方式利用的是.NET中静态关键字static的特性,使单例类在使用前被实例化,并且只实例化一次,这个由.NET框架保证。
这种方式存在问题,在没有使用到类中的成员时候就创建实例了,能否在使用到类成员的时候才创建实例呢?如下图
1 using System; 2 using System.Threading; 3 4 public class SingletonTest2 5 { 6 // 静态变量的方式实现单例模式 7 private static readonly Lazy<SingletonTest2> Instance = new Lazy<SingletonTest2>(() => new SingletonTest2()); 8 9 private SingletonTest2() 10 { 11 Thread.Sleep(1000); 12 Console.WriteLine("******初始化单例模式实例*****"); 13 } 14 15 public static SingletonTest2 GetInstance() 16 { 17 return Instance.Value; 18 } 19 20 public void PrintSomething() 21 { 22 Console.WriteLine($"当前线程Id为{Thread.CurrentThread.ManagedThreadId}"); 23 Console.WriteLine("Singleton Pattern Test"); 24 } 25 }
我们使用了Lazy关键字来延迟实例化。
四、例外
值得注意的是,反射会破坏单例模式,如下代码,能直接调用类的私有构造函数,再次实例化。
1 // 反射破坏单例 2 var singletonInstance = System.Activator.CreateInstance(typeof(SingletonTest2), true);
怎么避免呢?类的实例化都需要调用构造函数,那么我们在构造函数中加入判断标识即可。尝试实例化第二次的时候,就会抛异常。
1 private static bool isInstantiated; 2 3 private SingletonTest2() 4 { 5 if (isInstantiated) 6 { 7 throw new Exception("已经被实例化了,不能再次实例化"); 8 } 9 10 isInstantiated = true; 11 Thread.Sleep(1000); 12 Console.WriteLine("******单例类被实例化******"); 13 }
五、应用
那么实际应用中,哪些地方应该用单例模式呢?在这个类只应该存在一个对象的情况下使用。哪些地方用到了单例模式呢?
- Windows任务管理器
- HttpContext.Current
六、总结
俗话说,凡事都有两面性。单例模式确保了类只有一个实例,也引入了其他问题:
- 单例模式中的唯一实例变量是使用static标记的,会常驻内存,不被GC回收,长期占用了内存
- 在多线程的情况下,使用的都是同一个实例,所以需要保证类中的成员都是线程安全,不然可能会导致数据混乱的情况
代码下载:https://github.com/hzhhhbb/SingletonPattern
七、参考资料
- https://en.wikipedia.org/wiki/Singleton_pattern
- https://docs.microsoft.com/zh-cn/dotnet/api/system.lazy-1?view=netcore-3.0