一个假的单例模式,虽然有双重加锁的代码,但还是假的单例。

单例模式这东西简单,也是平时写代码最常用的一个模式了。虽然你知道单例模式的要点。
1.私有(private)静态(static)的单例对象(object)
2.构造函数(struct)私有化-----可在构造方法初始化单例对象
3.提供对外(public),静态方法获得单例对象
你也知道最常见的就是使用双重旋锁来判断对象是否已经初始化了,来控制有且只有一个单利会被初始化。

若是之前不太了解单例模式的话,可以再了解一下,有好几种实现方式呢,可以再看看。有不少注意事项呢!!!

单例模式的四种实现--应该是你见过的最全的单例模式实现啦。
但是,下面这个代码的单例模式有问题,看看能看出来哪有问题不?看不出问题的,还请继续往下看吧。

public class SenderFactory {
    private static final Map<String, Sender> SENDER_MAP = new ConcurrentHashMap<>();

    public static Sender getSenderByType(String type) {
        Sender sender = SENDER_MAP.get(type);
        if (sender == null) {
            synchronized (SenderFactory.class) {
                if (sender == null) {
                    sender = initSenderByType(type);
                    SENDER_MAP.put(type, sender);
                }
            }
        }
        return sender;
    }

    private static Sender initSenderByType(String type) {
        // xxx 省略,就是初始化各类型的sender的代码
        return sender;
    }
}

说明:
不知道,看出来没,初衷是弄个map来缓存使用到的所有的sender,一类sender只初始化一个就够用了。估计在一些不是spring的项目里面,也会使用类似的方法初始化一些service,缓存起来,方便使用。差不多一个道理。上面代码里面使用了双重旋锁,来判断单例,使用的是同步的map来缓存实例,加锁的对象就是当前类的class,在内存也是独此一份,先不说他没有添加private的构造,volatile 那个SENDER_MAP。即使volatile那个map,还是会初始化2个sender。

问题何在?为啥不是单例呢?感觉都是按照单例模式的套路走的。问题的关键在于“地址传递”问题,从map里面get一个sender出来的时候,要是get不到,那么代码里面的那个sender的地址就是null,虽然你在加锁的if里面初始化了单例,而且也put到缓存map去了,但是,也可能有线程已经跨过了第一个if null判断,在锁外面那层等着进互斥空间呢,假设这个线程是b,第一个线程a初始化完单例,出去之后,b进去了,因为没有更新sender的指向,还是null,所以,他还是会初始化单例的。所以,问题的关键就是在加锁的里面再次判断null的时候,需要再去map里面get一下,看看map里面是不是已经有了。这么滴就可以真正的单例了。

修正代码

    public static Sender getSenderByType(String type) {
        Sender sender = SENDER_MAP.get(type);
        if (sender == null) {
            synchronized (SenderFactory.class) {
                sender = SENDER_MAP.get(type);
                if (sender == null) {
                    sender = initSenderByType(type);
                    SENDER_MAP.put(type, sender);
                }
            }
        }
        return sender;
    }

额,好像也是挺简单的一个问题。。。。。。

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页