单例模式

52 阅读2分钟

单例模式是一种我们平时在代码开发中使用的一种常用设计模式,通过单例模式创建的类在当前进程中只有一个实例。

1. 饿汉式

在类加载的时候初始化静态实例,由JVM保证线程的安全性。

public class Singleton01 {

    private Singleton01() {

    }

    private static final Singleton01 instance = new Singleton01();

    public static Singleton01 getInstance() {
        return instance;
    }

}

2. 懒汉式(线程不安全)

在调用获取静态实例方法的时候才创建对象,如果是多线程的情况下会出现线程安全问题。

public class Singleton02 {

    private Singleton02() {

    }

    private static Singleton02 instance;

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

3. 懒汉式(线程安全)

使用synchronized锁保证线程安全。

public class Singleton03 {
    private Singleton03() {

    }

    private static Singleton03 instance;

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

4.双重检查

使用synchronized锁方法上保证线程安全,锁的粒度相对来说还是比较大的,这里我们优化成锁具体的代码块来减小锁的范围。

  1. volatile 保证instance变量的可见性和屏蔽指令重排序
  2. 在new Singleton04() 的时候可以分为下面三个步骤:1. 分配内存空间 2.初始化 3.将instance指向分配的内存地址。由于编译器优化将2、3步骤重排序,在高并发的情况下可能会有线程拿到还没有实例化的instance对象,从而造成了线程安全问题,所以这里我们用volatile 禁止指令重排序,保证所有线程拿到的都是完整的instance实例。
public class Singleton04 {
    private Singleton04() {

    }

    private static volatile Singleton04 instance;

    public static Singleton04 getInstance() {

        //第一次检查,如果instance不为空直接返回实例
        if (instance == null) {
            synchronized (Singleton04.class) {
                //有线程抢到锁后再次检查是否为空
                if (instance == null) {
                    instance = new Singleton04();
                }
            }
        }
        return instance;
    }
}

5.静态内部类

由JVM保证线程安全,在加载内部类的时候创建单例对象。

public class Singleton05 {


    private Singleton05() {

    }

    public static class SingletonInner {

        private static Singleton05 instance = new Singleton05();
    }

    public static Singleton05 getInstance() {
        return SingletonInner.instance;
    }

}

6.枚举方式

使用枚举方式实现起来简单,枚举实例的创建也是线程安全的。

public enum Singleton06 {

    INSTANCE;

    public static Singleton06 getInstance() {
        return INSTANCE;
    }
}