博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
剑指offer之面试题2:实现Singleton模式
阅读量:5038 次
发布时间:2019-06-12

本文共 6351 字,大约阅读时间需要 21 分钟。

来源:剑指offer

这篇主要记录《剑指offer》书籍中的面试题2:实现Singleton模式

使用语言:C# 

代码环境:VS2017

 

总共有5中解法,从前往后依次优化。

结构如下:

前言

这里先给出调用程序的代码

Program.cs

class Program    {        static void Main(string[] args)        {            Console.WriteLine("Hello World!");            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton1.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton1.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton2.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton2.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton3.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton3.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton4.Instance);            //});            //Task.Run(() =>            //{            //    Console.WriteLine(Singleton4.Instance);            //});                       Task.Run(() =>            {                Console.WriteLine(Singleton5.Instance);            });            Task.Run(() =>            {                Console.WriteLine(Singleton5.Instance);            });            Console.ReadKey();        }    }

这里,会在每次创建一种Singleton模式的实现方法之后,在这里调用。

里面会用两个线程来模拟多线程的情况。

而在单例的实现中,会在创建构造函数时,输出语句,来区别是否创建了多个对象。

效果如下示例:

构造函数只调用了一次。

 

方法一

单线程情况下的一般实现。

代码如下:

Singleton1.cs

1     public sealed class Singleton1 2     { 3         //私有的构造函数 4         private Singleton1() 5         { 6             //Console.WriteLine($"Singleton1生成了...{Guid.NewGuid()}"); 7         } 8         private static Singleton1 instance = null; 9         /// 10         /// 在静态属性Instance中,只有在instance为null时,才创建一个实例以避免重复11         /// 12         public static Singleton1 Instance13         {14             get15             {16                 //如果instance为空,则创建实例17                 if (instance == null)18                 {19                     //Thread.Sleep(3000);  //模拟多线程同时到达这里20                     instance = new Singleton1();21                 }22                 return instance;23             }24         }25     }

在上述代码中,Singleton1的静态属性Instance中,只有在instance为null的时候才创建一个实例以避免重复创建。

同时我们把构造函数定义为私有函数,这个就可以确保只创建一个实例。

但是这种方法只适合单线程,多线程情况下,就有问题了。

 

方法二

为了保证多线程环境下,我们还是只能得到一个类型的实例,需要加上一个同步锁。

代码如下:

Singleton2.cs

1     public sealed class Singleton2 2     { 3         ///  4         /// 私有的构造函数 5         ///  6         private Singleton2() 7         { 8             Console.WriteLine($"Singleton2生成了...{Guid.NewGuid()}"); 9         }10         //用作同步锁11         private static readonly object syncObj = new object();12 13         private static Singleton2 instance = null;14 15 16         public static Singleton2 Instance17         {18             get19             {20                 //添加同步锁21                 lock (syncObj)22                 {23                     //如果instance为null,则新建24                     if (instance == null)25                     {26                         Thread.Sleep(3000);27                         instance = new Singleton2();28                     }29                       30                 }31                 return instance;32             }33         }34     }

我们还是假设有两个线程同时想创建一个实例。由于在一个时刻只有一个线程能得到同步锁,

当第一个线程加上锁时,第二个线程只能等待。当第一个线程创建出实例之后,第二个线程就不会重复创建实例了,

这样就保证了我们在多线程环境中也只能得到一个实例。

 

但是呢,这种方法也不完美。我们每次通过属性Instance得到Singleton2的实例,都会试图加上一个同步锁,

而加锁时一个非常耗时的操作,在没有必要的时候我们应该尽量避免。

方法三

我们可以这样:加同步锁前后两次判断实例是否已经存在。

我们只是在实例还没有创建之前需要加锁操作,以保证只有一个线程创建出实例。而当实例已经创建之后,我们已经不需要再做加锁操作了。

改进代码如下:

Singleton3.cs

1     public sealed class Singleton3 2     { 3         //私有构造函数 4         private Singleton3() 5         { 6             Console.WriteLine($"Singleton3生成了...{Guid.NewGuid()}"); 7         } 8         //创建同步锁对象 9         private static readonly object syncObj = new object();10 11         private static Singleton3 instance = null;12 13         public static Singleton3 Instance14         {15             get16             {17                 //instance为null,这里可能有两个线程同时到达,即都判断为null的情况18                 if (instance == null)19                 {20                     Thread.Sleep(3000); //会同时有两个线程等在这里21                     //加上同步锁,即只放一个线程过去22                     lock (syncObj)23                     {24                         //如果此时instance还未null,则新建instance;否则,跳过25                         if(instance==null)26                             instance = new Singleton3();27                     }28                 }29                 return instance;30             }31         }32     }

这样的代码实现起来比较复杂,容易出错,我们还有更优秀的解法。

 

方法四

C# 语法中有一个函数能够保证只调用一次,那就是静态构造函数。

代码如下:

Singleton4.cs

1      public sealed class Singleton4 2     { 3         static Singleton4() //静态构造函数 4         { 5             Console.WriteLine($"Singleton4生成了...{Guid.NewGuid()}"); 6         } 7  8         private static Singleton4 instance = new Singleton4(); 9         10         public static Singleton4 Instance11         {12             get13             {14                 Thread.Sleep(3000);15                 return instance;16             }17         }18     }

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

C#中调用静态构造函数的时机不是由程序员掌控的,而是当.NET运行时,发现第一次使用一个类型的时候自动调用该类型的静态构造函数。

 

方法五

这种方法实现的Singleton,可以很好的解决 方法四 中Singleton4的实例创建时机过早的问题:

1     public sealed class Singleton5 2     { 3         Singleton5() 4         { 5             Console.WriteLine($"Singleton5生成了...{Guid.NewGuid()}"); 6         } 7  8         public static Singleton5 Instance 9         {10             get11             {12                 return Nested.instance;13             }14         }15 16         class Nested17         {18             static Nested()19             {20 21             }22             internal static readonly Singleton5 instance = new Singleton5();23         }24     }

在这段代码中,我们在内部定义了一个私有类型的 Nested。

当我们第一次用到这个嵌套类型的时候,会调用静态构造函数创建Singleton5的实例 instance。

类型Nested只在属性Singleton5.Instance中被用到,由于其私有属性,他人无法使用Nested类型。

因此,当我们第一次试图通过属性Singleton5.Instance得到Singleton5的实例时,会自动调用Nested的静态构造函数创建实例 instance。

 

总结

推荐解法,方法四,或者方法五

其中方法四利用了C#的静态构造函数的特性,确保只创建一个实例。

第五种方法利用了私有嵌套类型的特性,做到只在需要的时候才会创建实例,提高空间使用率。

 

转载于:https://www.cnblogs.com/Vincent-yuan/p/11361369.html

你可能感兴趣的文章
第四天 selenium的安装及使用
查看>>
关于js的设计模式(简单工厂模式,构造函数模式,原型模式,混合模式,动态模式)...
查看>>
KMPnext数组循环节理解 HDU1358
查看>>
android调试debug快捷键
查看>>
【读书笔记】《HTTP权威指南》:Web Hosting
查看>>
Inoodb 存储引擎
查看>>
数据结构之查找算法总结笔记
查看>>
Linux内核OOM机制的详细分析
查看>>
Android TextView加上阴影效果
查看>>
Requests库的基本使用
查看>>
C#:System.Array简单使用
查看>>
C#inSSIDer强大的wifi无线热点信号扫描器源码
查看>>
「Foundation」集合
查看>>
算法时间复杂度
查看>>
二叉树的遍历 - 数据结构和算法46
查看>>
类模板 - C++快速入门45
查看>>
[转载]JDK的动态代理深入解析(Proxy,InvocationHandler)
查看>>
centos7 搭建vsftp服务器
查看>>
RijndaelManaged 加密
查看>>
Android 音量调节
查看>>