单例模式:双重效验锁的懒汉实现方式

单例模式

单例模式顾名思义就是指实例化一个对象,通过把构造方法私有化来禁止创建实例。

饿汉模式实现

饿汉模式的特点是在类加载的时候就创建并初始化一个实例,实例在整个程序运行期间都是唯一的

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

这里的getInstance()必须是静态方法,因为静态方法可以通过类名.方法名的方式调用,而非静态方法则要创建实例,这与我们单例模式的规则不相符。

懒汉模式实现

懒汉模式的特点是需要的时候再创建实例,实例在整个程序运行期间都是唯一的。

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (null == instance) {
           instance = new Singleton();
        }
        return instance;
    }
}

上面代码在单线程模式下是没问题的,但是在多线程模式下就会存在线程安全问题。
如果在首次创建实例,多个线程同时调用getInstance()方法,就有可能创建多个实例。

改进1 (创建多个实例)

我们可以进行对 getInstance() 方法进行加锁:

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (null == instance) {
            instance = new Singleton();
        }
        return instance;
    }
}

这样虽然解决刚刚线程安全的问题,但每次调用getInstance()方法都要加锁,增加不少的开销。
我们发现上面线程安全问题只存在于首次创建实例的情况,因此我们只需要对instance为空的时候单独处理即可。

改进2 (性能比较低)

instance为空的时候加锁,再判断一次是否为空即可:

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

粗略的看上去好像没什么问题,实际上这里还有一个指令重排序的问题

通过查阅资料知道instance = new Singleton();这个代码在执行的时候实际是执行分3步骤执行:

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

JVM在执行的时候可能就会优化成 1->3->2的顺序执行
可能导致在多线程环境下,还没执行2就已经被其他线程返回了一个刚分配的地址
同样存在线程安全问题,需要使用volatile关键字来禁止指令重排序

改进3 (volatile禁止指令重排序)

public class Singleton {
    //volatile 防止指令重排 和可见性
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
         //先判断对象是否已经实例化过,没有实例化才进入加锁代码
        if (null == instance) {
            //类对象加锁
            synchronized (Singleton.class) {
                //避免 singleTon== null时,第一个线程实例化后,进入阻塞状态的线程被唤醒后仍会进行实例化。
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

以上就是线程安全的懒汉模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值