单例模式

关于设计模式想必大家都有所了解,设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、易于维护。当然这是百科的解释。

有的还叫设计模式design mode,个人理解就是多数程序员针对某些特定的问题,总结出来的一种规范的设计经验、或者编程的技巧。现在已经比较出名的有23种

创建型

 1. Factory Method(工厂方法)

 2. Abstract Factory(抽象工厂)

 3. Builder(建造者)

 4. Prototype(原型)

 5. Singleton(单例)

 结构型

 6. Adapter Class/Object(适配器)

 7. Bridge(桥接)

 8. Composite(组合)

 9. Decorator(装饰)

 10. Facade(外观)

 11. Flyweight(享元)

 12. Proxy(代理)

 行为型

 13. Interpreter(解释器)

 14. Template Method(模板方法)

 15. Chain of Responsibility(责任链)

 16. Command(命令)

 17. Iterator(迭代器)

 18. Mediator(中介者)

 19. Memento(备忘录)

 20. Observer(观察者)

 21. State(状态)

 22. Strategy(策略)

 23. Visitor(访问者)

单例模式 Singleton

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用性: 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

适合只有一个对象的场景整个程序运行期间只有唯一一个对象,要求对象只被实例化一次。比如打印机的连接、数据库连接池、线程池、写日志对象。

第一种写法

public sealed class Singleton
{
private static int BuildCount=0;
private static int CreateCount=0;
private static int MethodCount=0;
//内部静态对象
private static Singleton singleton=null;
//私有化构造函数,不让外面创建对象
private Singleton()
{
Singleton.BuildCount++;
Console.WriteLine("第{0}次执行构造函数",Singleton.BuildCount);
}
//静态方法获取对象实例
public static Singleton CreateInstance()
{
if(singleton==null)
{ singleton
=new Singleton();
} Singleton.CreateCount
++; Console.WriteLine("第{0}次执行CreateInstance",Singleton.CreateCount); return singleton; } public void Log() { Singleton.MethodCount++; Console.WriteLine("我是第{0}次调用,但是我是第{1}次构造函数的对象",Singleton.MethodCount,Singleton.BuildCount); } } }
class Program
{

static void Main(string[] args)
{

for(int i=0;i<9;i++)
{
Singleton.CreateInstance();
}
Console.ReadKey(); 
}
}

单线程执行之后,你会发现构造函数只执行了一次,而静态实例方法执行了很多次,也就是相当于实例化了一次,那么你认为已经完成了,其实并没有实现。

class Program
{

static void Main(string[] args)
{

TaskFactory task=new TaskFactory();
for(int i=0;i<9;i++)
{
//Singleton.CreateInstance();//单线程

task.StartNew(()=>Singleton.CreateInstance());//多线程
}

Console.ReadKey();
}

}

多线程执行后,你会发现执行了3次或4次构造函数,说明不是单例了。为什么这里执行了4次,而不是10次,这个其实和多线程有关,我们来说一下执行过程,首先我们准备了10个线程都去创建对象,多线程是并发执行,都进来去判断对象是否是null,而我们的电脑一般是4线程的,所以只有4个线程进来判断对象null,也就执行了4次构造函数,如果电脑够快有可能是8次或10次。

我们给静态方法里面加一个锁

public sealed class Singleton
{
private static int BuildCount=0;
private static int CreateCount=0;
private static int MethodCount=0;
//内部静态对象
private static Singleton singleton=null;
//私有化构造函数,不让外面创建对象
private Singleton()
{
Singleton.BuildCount++;
Console.WriteLine("第{0}次执行构造函数",Singleton.BuildCount);
}

private static object Singleton_Lock=new object();
//静态方法获取对象实例
public static Singleton CreateInstance()
{
   lock (Singleton_Lock)
   {
      if(singleton==null)
       {
        singleton=new Singleton();
       }
   }
    Singleton.CreateCount++;
   Console.WriteLine("第{0}次执行CreateInstance",Singleton.CreateCount);
    return singleton;
}

public void Log()
{
Singleton.MethodCount++;
Console.WriteLine("我是第{0}次调用,但是我是第{1}次构造函数的对象",Singleton.MethodCount,Singleton.BuildCount);
}

 
}
}

这样是完全可以的,但是还不完美,我们应该再加一层判断

//静态方法获取对象实例
public static Singleton CreateInstance()
{
if(singleton==null)
{
   lock (Singleton_Lock)
   {
      if(singleton==null)
       {
        singleton=new Singleton();
       }
   }
}
    Singleton.CreateCount++;
   Console.WriteLine("第{0}次执行CreateInstance",Singleton.CreateCount);
    return singleton;
}

为什么要再加一层判断,在多线程情况下你会发现,同时有4个进程进入到锁里面,这时只有一个进程去创建,而其他进程再去创建时发现已被创建,就会返回,而这会浪费进程等待时间。如果再有一个多线程又去创建对象,很显然前面这个对象已经被创建过了,这时依然还会有4个进程去等待。所以在外层的if判断不仅判断对象是否为空,更重要的是第一次创建之后,不再等待锁,而里面的if保证了单例的实现。

第二种写法

主要运用语言的特性,静态构造函数:该类第一次被使用的时候执行,且只执行一次

public  class Singleton
{
//内部静态对象
private static Singleton singleton=null;
//私有化构造函数,不让外面创建对象
private Singleton()
{

}
//静态构造函数:该类第一次被使用的时候执行,且只执行一次
static Singleton()
{
  singleton=new Singleton();
  
}
//静态方法获取对象实例
public static Singleton CreateInstance()
{

    return singleton;
}

第三种写法

public  class Singleton
{
//内部静态对象 只会创建一次,在内存中有且只有一个
private static readonly Singleton singleton=new Singleton();
//私有化构造函数,不让外面创建对象
private Singleton(){}
//静态方法获取对象实例
public static Singleton CreateInstance()
{

    return singleton;
}
}

当然第三种你可以直接把静态对象开放出去,但不建议这么用,如果要这样用一定把权限控制好。




原文地址:https://www.cnblogs.com/inconceivable/p/5536631.html