单例设计模式 (1)

emmm, 单例应该是最常见的设计模式了,我们比较简单的写法就是延迟初始化的写法,昨天在看《Java并发编程实战》的时候,发现原来延迟初始化是一种线程不安全的写法,回寝室的路上想了想,早上也看了些资料,今天简单做个总结。

#####1.延迟初始化

@NotThreadSafe
public class Singleton {
    private Singleton() {} 
    private static Singleton instance = null; 
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
复制代码

如果单例初始值是null,则new一个单例并返回。这个写法属于单例模式当中的懒汉模式。如果我们一开始写成new Singleton(),则不再需要判空操作,这种写法属于饿汉模式。这种方法在单线程的情况是可以,但在多线程的情况下,是不安全。

原因如下: 假设我们Singleton类刚刚被初始化,instance对象为空,这时候两个线程同时访问到了getInstance方法,那他们都会通过if判断,然后会都会返回给instance,那我们两个getInstance可能得到不同的结果

#####2.简单修改

@ThreadSafe
public class Singleton {
    private Singleton() {} 
    private static Singleton instance = null; 
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
复制代码

最简单的解决方法当然是直接对整个方法加synchronized关键字,但是这样效率会很低,我们要尽量避免对整个方法加锁,所以可以想到用操作系统里面的双重检测修改后如下

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

一开始我以为我也以为这样处理就行了,很多博客里面都说这种也已经是线程安全的写法,然后有人在评论指出了这种其实也不是绝对线程安全。

原因如下: 虽然看起来是可以的,但JVM编译器会进行指令重排,比如 instance = new Singleton,会被编译器编译成如下JVM指令:

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

但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:

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

如果多个线程同时访问的时候,A线程执行到了13,B线程进行if判断,虽然instance还没初始化,但他已经不为空,B线程就会返回一个未初始化的instance,防止指令重排有什么办法? 加volatile注解啊。所以只要定义变量的时候写为: private volatile static Singleton instance = null; 就可以了。

转载于:https://juejin.im/post/5a31227a6fb9a04517053353

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值