学习之Java学习

单例模式-懒汉式-Java双重校验锁🔒

2021-06-07  本文已影响0人  千夜零一

单例模式

饿汉式:

public class SingletonE {

    /**
     * ----饿汉式----
     * 是否 Lazy 初始化:否
     * 是否多线程安全:是
     * 实现难度:易
     */
    private static SingletonE instance = new SingletonE();
    private SingletonE(){}
    public static SingletonE getInstance(){
        return instance;
    }
}

懒汉式:

public class SingletonL {
    private static SingletonL instance;
    private SingletonL(){ }

    /**
     * <==="懒汉式"===>
     * 是否 Lazy 初始化:是
     * 是否多线程安全:否
     * 实现难度:易
     */
//    public SingletonL getInstance(){
//        if (null == instance){
//            instance = new SingletonL();
//        }
//        return instance;
//    }

    /**
     * <==="懒汉式"-线程安全===>
     * 是否多线程安全:是
     * 实现难度:易
     * 缺点:线程安全了,但每次实例化需要检查锁,性能低。
     */
//    public static synchronized SingletonL getInstance() {
//        if (null == instance){
//            instance = new SingletonL();
//        }
//        return instance;
//    }
}

双重校验锁🔒

public class SingletonDCL {
    /**
     * <==="懒汉式"--双重校验锁--===>
     * JDK 版本:JDK1.5 起
     * 是否 Lazy 初始化:是
     * 是否多线程安全:是
     * 实现难度:较复杂
     */

    private static volatile SingletonDCL instance;
    private SingletonDCL(){}

    public static  SingletonDCL  getInstance() {
        if(null == instance){
            synchronized (SingletonDCL.class){
                if(null == instance){
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
    /**
     * 相关问题:
     1、为什么synchronized关键字要放在SingletonDCL getInstance()方法内?
     2、为什么要在synchronized修饰代码段内再次加校验?
     3、为什么要对SingletonDCL.class加锁,而不是局部变量?
     4、为什么要使用volatile关键字修饰instance变量?
     5、双重校验锁第一次进行判空原因?
     6、双重校验锁第二次进行判空原因?
     */
}

问题解惑:

答:由于synchronized加在了getInstance方法内,因此导致线程不安全了,若要使线程安全,需要在synchronized修饰的代码块内再次进行if(null == instance){}校验,保证线程安全。

答:首先要明确的一个点,就是synchronized关键字可以修饰什么?变量、代码块段、方法、对象。都可以,因为A线程、B线程其实都是对这个单例类进行访问,因此需要锁的是这个类对象!

答:首先明确一个点:不加volatile线程就不安全了吗?当然不是,无论加不加volatile关键字,此时线程都是安全的,并且性能也高!那为什么要加volatile呢?禁止指令重排序!不被volatile修饰前,代码的执行顺序可能会改变,但被修饰之后,指令重排序被禁止。

答:假设有两个线程A和B,都进行完第一次判空了,A和B都阻塞在synchronized (SingletonDCL.class){……}之前。因此需要二次判空,防止阻塞。

上一篇下一篇

猜你喜欢

热点阅读