【面试题2】实现Singleton模式

【题目描述】

设计一个类,我们只能生成该类的一个实例。

【解决方案】

1. 只适用于单线程环境

单线程环境下可以正常工作。

但是在多线程环境下,如果两个线程同时判断到instance为null,那么这两个线程都会创建一个实例,此时的Singleton就不满足单例模式的要求了。

 1     public sealed class Singleton
 2     {
 3         private Singleton()
 4         { 
 5         }
 6 
 7         private static Singleton instance = null;
 8         public static Singleton Instance
 9         {
10             get
11             {
12                 if (instance == null)
13                     instance = new Singleton();
14                 return instance;
15             }            
16         }
17     }

2. 多线程环境能工作,但效率不高

加同步锁处理后,能保证在多线程环境中也只创建一个实例。

但是由于在调用属性Instance时都会加同步锁,加锁又是一个非常耗时的操作,在没有必要的时候我们应该尽量避免。

 1     public sealed class Singleton
 2     {
 3         private Singleton()
 4         {
 5         }
 6 
 7         private static readonly Object syncObj = new object();
 8 
 9         private static Singleton instance = null;
10         public static Singleton Instance
11         {
12             get
13             {
14                 lock (syncObj)
15                 {
16                     if (instance == null)
17                         instance = new Singleton();
18                 }
19                 return instance;
20             }
21         }
22     }

 3. 加同步锁前后两次判断实例是否已经存在

加锁之前进行判断实例是否已经创建,可以保证实例已经创建后,则可以直接返回instance,而无需去进行加锁操作,保证只创建一个实例,相比上述方法,也提高了效率。

由于此代码实现比较繁杂且容易出错,我们可以尝试更好的解决方案。

 1     public sealed class Singleton
 2     {
 3         private Singleton()
 4         {
 5         }
 6 
 7         private static readonly Object syncObj = new object();
 8 
 9         private static Singleton instance = null;
10         public static Singleton Instance
11         {
12             get
13             {
14                 if (instance == null)
15                 {
16                     lock (syncObj)
17                     {
18                         if (instance == null)
19                             instance = new Singleton();
20                     }
21                 }
22                 return instance;
23             }
24         }
25     }

4. 利用静态构造函数

由于C#是在调用静态构造函数时初始化静态变量,.NET运行时能够确保只调用一次静态构造函数,这样我们就可以确保只初始化一次instance。

但是,根据静态构造函数的特性,实例instance并不是第一次调用属性Singleton.Instance时创建,而是在第一次用到Singleton的时候就会被创建。假设我们在Singleton中添加一个静态方法调用该静态方法是不需要创建实例的,但是由于你使用了Singleton,他会自动调用静态构造函数并创建实例,造成过早地创建实例,从而降低内存的使用效率。

 1     public sealed class Singleton
 2     {
 3         private Singleton()
 4         {
 5         }
 6 
 7         private static Singleton instance = new Singleton();
 8         public static Singleton Instance
 9         {
10             get
11             {
12                 return instance;
13             }
14         }
15     }

5. 实现按需创建实例

内部定义一个私有类型Nested,类型Nested只在调用Singeton.Instance时被使用,由于其私有属性他人无法使用Nested类型。

因此当我们第一次调用Sington.Instance时,会调用Nested的静态构造函数,并初始化静态变量。如果我们不调用属性Sington.Instance,则不会创建instance实例,这样就做到了按需创建。

 1     public sealed class Singleton
 2     {
 3         Singleton()
 4         {
 5         }
 6 
 7         public static Singleton Instance
 8         {
 9             get
10             {
11                 return Nested.instance;
12             }
13         }
14 
15         class Nested
16         {
17             static Nested()
18             {
19             }
20             internal static readonly Singleton instance = new Singleton();
21         }
22     }

【本题扩展】

五种单例模式的实现把类型标记为sealed,表示他们不能作为其他类型的基类。现在我们要定义一个表示总统的类型President,可以从该类型继承出FrenchPresident和AmericanPresident等类型,这些派生类都只能产生一个实例。请问该如何设计实现这些类型?

我的代码实现,仅供参考:

 1     class President
 2     {
 3     }
 4 
 5     sealed class FrenchPresident:President
 6     {
 7         FrenchPresident()
 8         {
 9         }
10 
11         public static FrenchPresident Instance
12         {
13             get
14             {
15                 return Nested.instance;
16             }
17         }
18 
19         class Nested
20         {
21             static Nested()
22             {
23             }
24             internal static readonly FrenchPresident instance = new FrenchPresident();
25         }
26     }
27 
28     sealed class AmericanPresident : President
29     {
30         AmericanPresident()
31         {
32         }
33 
34         public static AmericanPresident Instance
35         {
36             get
37             {
38                 return Nested.instance;
39             }
40         }
41 
42         class Nested
43         {
44             static Nested()
45             {
46             }
47             internal static readonly AmericanPresident instance = new AmericanPresident();
48         }
49     }

 

posted @ 2015-09-09 15:42  叫我霍啊啊啊  阅读(491)  评论(0编辑  收藏  举报