23种设计模式之单例(Singleton Pattern)

单例前端

在软件系统中,常常有这样一些特殊的类,必须保证它们在系统中只存在一个实例(eg:应对一些特殊状况,好比数据库链接池(内置了资源)  全局惟一号码生成器),才能确保它们的逻辑正确性、以及良好的效率。数据库

优势:单例的好处就是单例,就是全局惟一的一个实例
单例模式会阻止其余对象实例化其本身的单例对象的副本,从而确保全部对象都访问惟一实例安全

缺点:单例能够避免重复建立,可是也会常驻内存 除非是真的有必要,不然不要单例并发

使用单例对象(尤为在类库中定义的对象)时,开发人员必须记住本身不能使用new关键字实例化对象。由于可能没法访问库源代码,所以应用程序开发人员可能会意外发现本身没法直接实例化此类ide

如何绕过常规的构造器,提供一种机制来保证一个类只建立一个实例?函数

如何实现?将构造函数私有化,而后对外提供一个公开的静态方法,使用一个静态属性进行判断当前对象是否被建立测试

 1 // 不要用这种方式
 2 public class Singleton
 3 {
 4     private static Singleton _instance = null;
 5     private Singleton() { }
 6     public static Singleton CreateInstance()
 7     {
 8         if (_instance == null)
 9         {
10             _instance = new Singleton();
11         }
12         return _instance;
13     }
14 }
View Code

上面的方法是非线程安全的,多个不一样的线程能够同时进入这个方法,若是instance为空的而且这里返回真的状况下,均可以建立实例,这显然违反了单例模式,实际上,在测试之前,实例就已经有可能被建立了,可是内存模型不能保证这个实例能被其余的线程看到,除非合适的内存屏障已经被跨过了优化

咱们把上面的代码优化一下this

 1     /// <summary>
 2     /// 单例类:一个构造对象很耗时耗资源类型
 3     /// 懒汉式单例模式
 4     /// </summary>
 5     public class Singleton
 6     {
 7         /// <summary>
 8         /// 构造函数耗时耗资源
 9         /// </summary>
10         private Singleton()
11         {
12             long lResult = 0;
13             for (int i = 0; i < 10000000; i++)
14             {
15                 lResult += i;
16             }
17             Thread.Sleep(2000);
18             Console.WriteLine("{0}被构造一次", this.GetType().Name);
19         }
20         /// <summary>
21         /// 3 全局惟一静态  重用这个变量
22         /// </summary>
23         private static volatile Singleton _Singleton = null;
24         //volatile 促进线程安全 让线程按顺序操做
25         private static readonly object Singleton_Lock = new object();
26         /// <summary>
27         /// 2 公开的静态方法提供对象实例
28         /// </summary>
29         /// <returns></returns>
30         public static Singleton CreateInstance()
31         {
32             if (_Singleton == null)//是_Singleton已经被初始化以后,就不要进入锁等待了
33             {
34                 lock (Singleton_Lock)
35                 //保证任意时刻只有一个线程进入lock范围
36                 //也限制了并发,尤为是_Singleton已经被初始化以后
37                 {
38                     //Thread.Sleep(1000);
39                     //Console.WriteLine("等待锁1s以后才继续。。。");
40                     if (_Singleton == null)//保证只实例化一次
41                     {
42                         _Singleton = new Singleton();
43                     }
44                 }
45             }
46             return _Singleton;
47         }
48 
49         //既然是单例,你们用的是同一个对象,用的是同一个方法,那还会并发吗  还有线程安全问题吗?
50         public int iTotal = 0;
51         public void Show()
52         {
53             //lock (Singleton_Lock)
54             //{
55                 this.iTotal++;
56             //}
57         }
58 
59         public static void Test()
60         {
61             Console.WriteLine("Test1");
62             Console.WriteLine(_Singleton.iTotal);
63         }
64 
65     }
View Code

前端调用spa

 1                {
 2                     List<Task> tasks = new List<Task>();
 3                     for (int i = 0; i < 10000; i++)
 4                     {
 5                         tasks.Add(Task.Run(() =>
 6                         {
 7                             Singleton singleton = Singleton.CreateInstance();
 8                             singleton.Show();
 9                         }));
10                     }
11                     Task.WaitAll(tasks.ToArray());
12                     Singleton.Test();
13                     //iTotal 是0  1   10000  仍是其余的
14                     //其余值,1到10000范围内均可能   线程不安全
15 
16                 }
View Code

 运行代码咱们会发现一个问题

iTotal 是0  1   10000  仍是其余的,
其余值,1到10000范围内均可能   线程不安全
为何呢?形成这种状况的缘由单例执行singleton.Show()方法时 iTotal在等于某个值时被附加屡次,由此获得结论:
即便是单例,变量也不是线程安全的,单例不是为了保证线程安全
如何优化?给show方法加把锁
1         public void Show()
2         {
3             lock (Singleton_Lock)
4             {
5                 this.iTotal++;
6             }
7         }
View Code

单例还有另外的写法,以上是懒汉式单例模式,下面咱们来看看饿汉式

利用静态构造函数 程序第一次使用这个类型前被调用,且只调用一次

 1     /// <summary>
 2     /// 单例类:一个构造对象很耗时耗资源类型
 3     /// 
 4     /// 饿汉式
 5     /// </summary>
 6     public class SingletonSecond
 7     {
 8         /// <summary>
 9         /// 1 构造函数耗时耗资源
10         /// </summary>
11         private SingletonSecond()
12         {
13             long lResult = 0;
14             for (int i = 0; i < 10000000; i++)
15             {
16                 lResult += i;
17             }
18             Thread.Sleep(1000);
19             Console.WriteLine("{0}被构造一次", this.GetType().Name);
20         }
21         /// <summary>
22         /// 静态构造函数:由CLR保证,程序第一次使用这个类型前被调用,且只调用一次
23         /// 
24         /// 检测,初始化
25         /// 写日志功能的文件夹检测
26         /// XML配置文件
27         /// </summary>
28         static SingletonSecond()
29         {
30             _SingletonSecond = new SingletonSecond();
31             Console.WriteLine("SingletonSecond 被启动");
32         }
33 
34 
35         private static SingletonSecond _SingletonSecond = null;
36         public static SingletonSecond CreateInstance()
37         {
38             return _SingletonSecond;
39         }//饿汉式  只要使用类就会被构造
40 
41         
42     }
View Code

另一种相似的,利用静态字段建立对象

 1     /// <summary>
 2     /// 单例类:一个构造对象很耗时耗资源类型
 3     /// 饿汉式
 4     /// </summary>
 5     public class SingletonThird
 6     {
 7         /// <summary>
 8         /// 构造函数耗时耗资源
 9         /// </summary>
10         private SingletonThird()
11         {
12             long lResult = 0;
13             for (int i = 0; i < 10000000; i++)
14             {
15                 lResult += i;
16             }
17             Thread.Sleep(1000);
18             Console.WriteLine("{0}被构造一次", this.GetType().Name);
19         }
20 
21         /// <summary>
22         /// 静态字段:在第一次使用这个类以前,由CLR保证,初始化且只初始化一次
23         /// 这个比今天构造函数还早
24         /// </summary>
25         private static SingletonThird _SingletonThird = new SingletonThird();//打印个日志
26         public static SingletonThird CreateInstance()
27         {
28             return _SingletonThird;
29         }//饿汉式  只要使用类就会被构造
30 
31 
32 
33 
34         public void Show()
35         {
36             Console.WriteLine("这里是{0}.Show", this.GetType().Name);
37         }
38 
39     }
View Code

 本文参考文档:https://csharpindepth.com/Articles/Singleton#unsafe

相关文章
相关标签/搜索