[设计模式]你不是对单例模式比较熟吗?你倒是写一个线程安全的出来呀!

单例模式比较好理解,就是保证只有一个对象被创建

如果是单线程下代码还比较好写一些:

/**
 *饿汉模式
 * 单例实例在类装载时进行创建
 * 线程安全
 */
public class SingletonExample2 {
     
    // 私有构造函数
    private SingletonExample2(){
     

    }
    // 单例对象
    private static SingletonExample2 instance = new SingletonExample2();
    // 静态的工厂方法
    public static SingletonExample2 getInstance(){
     
        return instance;
    }
}

上面的是饿汉模式,在类加载时就开始创建
懒汉模式就比较懒,在使用的时候才会创建

/**
 *懒汉模式
 * 单例实例在第一次使用时进行创建
 * 线程安全--并不推荐,因为带来了性能上的开销
 */
public class SingletonExample3 {
     
    // 私有构造函数
    private SingletonExample3(){
     

    }
    // 单例对象
    private static SingletonExample3 instance = null;
    // 静态的工厂方法
    public static synchronized SingletonExample3 getInstance(){
     
        if (instance == null){
     
            instance = new SingletonExample3();
        }
        return instance;
    }
}

如果是在单线程环境下,上面的代码是没有任何问题的
但是如果是在多线程环境下呢?
现在同时有线程 A 和 B 要创建线程,此时线程 A 拿到了 instance 的值为 null ,然后 CPU 停止了当前线程 A ,线程 B 开始运行,也拿到了 instance 的值为 null ,接下来线程 A 和 B 都会去创建
怎么办呢?加个双重检测机制,再加个锁

/**
 *懒汉模式-->双重同步锁模式
 * 单例实例在第一次使用时进行创建
 * 线程不安全
 */
public class SingletonExample4 {
     
    // 私有构造函数
    private SingletonExample4(){
     

    }
    // 1, memory = allocate() 分配对象的内存空间
    // 2, ctorInstance() 初始化对象
    // 3, instance = memory 设置 instance 指向刚分配的内存

    // JVM 和 cpu 优化时,会发生指令重排
    // 有可能发生 1,3,2
    //此时会导致线程不安全

    // 单例对象
    private static SingletonExample4 instance = null;
    // 静态的工厂方法
    public static SingletonExample4 getInstance(){
     
        if (instance == null){
        // 使用了双重检测机制
            synchronized (SingletonExample4.class) {
        // 同步锁
                if(instance == null) {
     
                    instance = new SingletonExample4();
                }
            }
        }
        return instance;
    }
}

这样双重检测再加锁看似是没有问题了
但是还记得吗? JVM 是会指令重排的,本来 1,2,3 运行下来没问题,结果呢,指令重排之后就变成了 1,3,2 ,还是两个线程,线程 A 拿到第一个的 instance 为 null ,线程 B 拿到的是指令重排之后的第二个 instance 为 null ,此时仍然有线程不安全的问题
那我不让它指令重排不就好了? volatile 就要大显身手了

/**
 *懒汉模式-->双重同步锁模式
 * 单例实例在第一次使用时进行创建
 */
public class SingletonExample5 {
     
    // 私有构造函数
    private SingletonExample5(){
     

    }
    // 1, memory = allocate() 分配对象的内存空间
    // 2, ctorInstance() 初始化对象
    // 3, instance = memory 设置 instance 指向刚分配的内存

    // JVM 和 cpu 优化时,会发生指令重排
    // 有可能发生 1,3,2
    //此时会导致线程不安全

    // 单例对象--- volatile + 双重检测机制-->禁止指令重排
    private  volatile static SingletonExample5 instance = null;
    // 静态的工厂方法
    public static SingletonExample5 getInstance(){
     
        if (instance == null){
        // 使用了双重检测机制
            synchronized (SingletonExample5.class) {
        // 同步锁
                if(instance == null) {
     
                    instance = new SingletonExample5();
                }
            }
        }
        return instance;
    }
}

上面都是对懒汉模式的优化,饿汉模式也想线程安全,该咋办嘞

/**
 *饿汉模式
 * 单例实例在类装载时进行创建
 * 线程安全--->要注意 private static 和 static 的先后执行顺序
 */
public class SingletonExample6 {
     
    // 私有构造函数
    private SingletonExample6(){
     

    }
    // 单例对象
    private static SingletonExample6 instance = null;
    static{
     
        instance = new SingletonExample6();
    }
    // 静态的工厂方法
    public static SingletonExample6 getInstance(){
     
        return instance;
    }

    public static void main(String[] args) {
     
        System.out.println(getInstance().hashCode());
        System.out.println(getInstance().hashCode());
    }
}

还有一种最安全的方法:

/**
 * 线程安全---最安全
 * 推荐使用
 */
public class SingletonExample7 {
     
    // 私有构造函数
    private SingletonExample7(){
     

    }

    public static SingletonExample7 getInstance(){
     
        return Singleton.INSTANCE.getInstance();
    }
    private enum Singleton{
     
        INSTANCE;
        private SingletonExample7 singleton;
        // JVM 保证这个方法绝对只调用一次
        Singleton(){
     
            singleton = new SingletonExample7();
        }
        public SingletonExample7 getInstance(){
     
            return singleton;
        }
    }
}

以上,感谢您的阅读哇~

你可能感兴趣的:([设计模式]你不是对单例模式比较熟吗?你倒是写一个线程安全的出来呀!)