单例模式

63 阅读3分钟

什么是单例模式

对象重用的需求:

  • 对象在构造的时候会消耗时间或者占用资源
  • 很多地方都需要用到这个对象
  • 不同地方的使用一个实例和多个实例没有区别

对象重用的实现:

  • 同一个方法或同一个类:变量共享即可
  • 不同类:公开静态变量共享即可

但是,并没有强制保证只有一个实例

强制保证某个类型只有一个实例,这就是单例。

,一个进程只需要一个实例,多个实例反而可能出问题

单例和普通实例的比较

  • 对象覆盖:单例会覆盖对象,多次调用会互相影响。而普通实例多次调用互不干扰
  • 线程安全:单例并不能保证实例中的方法是线程安全的,即单例、普通实例都不是线程安全的
  • 对象个数:单例只有1个对象进行重用,普通实例多个对象,即时创建
  • 内存占用:单例常驻内存,普通实例用完释放
  • 易用性:单例不能new(),普通实例直接new()

单线程的单例模式

  • 私有化构造函数--避免外界重复构造
  • 公开静态方法提供实例--这样外界才能使用
  • 静态变量共享--保证全局唯一
public class Sinleton {
    private Sinleton() { 
        //...
    } 
    private static Sinleton Instance=null;
    public static Sinleton CreateInstance() {
        if(Instance==null)
            Instance = new Sinleton();
        return Instance;
    }
}

上端调用

Sinleton sinleton = Sinleton.CreateInstance();

多线程的单例模式

上面的实例,在多线程下仍然会重复构造,因为并发了,在对象被初始化之前,判断失效,所以会被重复初始化

总结:推荐采用静态构造方法或静态字段的方式

方案一:加锁

//1、定义一个readonly的object对象
private static readonly object Sinleton_Lock=new object();
//2、将构造对象的语句加锁
lock (Sinleton_Lock) {//保证方法块只有一个线程可以进入
    if (Instance == null)
        Instance = new Sinleton();
}

上端,多线程调用

//启动线程
Task.Run(() => {
    Sinleton sinleton = Sinleton.CreateInstance();
});

缺点:这其实是担心并发安全就拒绝多线程,又重回单线程,并不是最优的解决方案

方案二:双判断锁

这是一个很经典的写法,但写起来较复杂

private static readonly object Sinleton_Lock=new object();
public static Sinleton CreateInstance() {
    if (Instance == null) {//双检锁:对象已经实例化后,不再等待锁
        lock (Sinleton_Lock) {//保证方法块只有一个线程可以进入
            if (Instance == null)
                Instance = new Sinleton();
        }
    }
    return Instance;
}

懒汉式、饿汉式

懒汉式:用到这个实例的时候,才会被构造,即双检锁

饿汉式:只要使用类中的任何东西,类的实例都会被构造,即静态构造函数、静态字段

方案三:静态构造函数

静态构造函数,由CLR调用,在类型第一次被使用前调用,且只调用一次。在单线程、多线程下都生效

public class Sinleton {
    private Sinleton() { 
        //...
    } 
    private static Sinleton Instance=null;
    static Sinleton() {
        Instance = new Sinleton();
    }
    public static Sinleton CreateInstance() {
        return Instance;
    }
}

方案四:静态字段

静态字段,由CLR调用,在类型第一次被使用前初始化,且只初始化一次!

静态字段先构造,静态构造函数再构造

public class Sinleton {
    private Sinleton() { 
        //...
    }
    private static Sinleton Instance= new Sinleton();
    public static Sinleton CreateInstance() {
        return Instance;
    }
}