设计模式------单例模式(Singleton Pattern)

1. 什么是单例模式

单例模式一般涉及单一的一个类,该类负责创建自己的对象,同时确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类被称为单例类,它提供全局访问的方法

其类图如下:

设计模式------单例模式(Singleton Pattern)_第1张图片

通过类图我们可以发现几个注意的点:

  • 构造器应为 private。因为单例模式就是为了让一个类仅有一个实例,所以需要防止外部创建类实例,仅通过静态方法调用。
  • 通过 public 的 getInstance() 方法获取实例,并且该方法应为 static 的。因为一个类只会有一个实例。

 

2. 单例模式的分类

我们根据是否懒加载将单例模式分为饿汉式懒汉式

2.1. 饿汉式

/**
 * Created by vMars on 2019/9/24.
 * 饿汉式
 * 这样是线程安全的
 */
public class SingleObject_Hungry {
    //创建 SingleObject_Hungry 的一个对象
    private static SingleObject_Hungry instance = new SingleObject_Hungry();

    //让构造函数为 private,这样该类就不能在外部实例化
    private SingleObject_Hungry(){}

    //获取唯一可用的对象
    public static SingleObject_Hungry getInstance(){
        return instance;
    }
}

懒汉式不管我们是否有使用到该类的实例,只要该类被加载了,就会先创建好一个静态实例对象。

  • 优点:
  1. 懒汉式是线程安全的,不用加锁操作,效率更高。
  • 缺点:
  1. 因为不是懒加载,如果我们并不需要用到该类的实例可能会造成空间浪费。

 

2.2. 懒汉式

/**
 * Created by vMars on 2019/10/10.
 * 懒汉式
 */
public class SingleObject_Lazy {

    private static SingleObject_Lazy instance;

    private SingleObject_Lazy() {};

    public static synchronized SingleObject_Lazy getInstance() {
        if(instance == null) {
            instance = new SingleObject_Lazy();
        }
        return instance;
    }
}

懒汉式则是在我们调用 getInstance() 去获取类实例时判断是否已经new一个实例了,没有则new一个。

这样在多线程的情况下就可能出现两个线程同时去new实例,这是我们所不希望的,所以对 getInstance() 我们可以加锁。

但是这样的效率会很低,如当我们的静态实例已经创建了,但由于 getInstance() 加了锁,所以每次还是只能有一个线程调用。

为了降低锁的细粒度,我们可以使用双重锁校验式

/**
 * Created by vMars on 2019/9/29.
 * 双重锁校验式——懒汉式
 */
public class SingleObject_Lazy {

    private volatile static SingleObject_Lazy instance;

    private SingleObject_Lazy() {};

    //双重锁校验
    public static SingleObject_Lazy getInstance() {
        if (instance == null) {
            synchronized (SingleObject_Lazy.class) {
                if (instance == null) {
                    instance = new SingleObject_Lazy();
                }
            }
        }
        return instance;
    }
}

双重锁校验式没有直接对 getInstance() 加锁,而是先判断 instance 是否为空,如果是则再对类加锁进行实例化。

这里我们注意到在加了锁之后还需要再判断一次 instance 是否为空,这是为了防止有多个线程通过第一个 instance 为空的判断,然后为该类重复实例多次。

这里还要注意 instance 应用 volatile 修饰,这是为了防止其指令重排,我们知道 new 的操作不是原子性的,大概可以分为三步,当我们在这三步之间去访问这个对象是会报异常的。

(不懂为啥会报异常的可以看:https://www.cnblogs.com/goodAndyxublog/p/11356402.html)

你可能感兴趣的:(设计模式)