vlambda博客
学习文章列表

创建一个单例模式有多难?

春节过完了,家里蹲久了,该刷的剧不该刷的剧都已经mission complete了。唯独翻开夹着小纸条的书本的时候,想起了信誓旦旦曾经发过的誓言,庆幸自己没有附带些恶毒的惩罚措施。

时间久了,头发没长多少,对基本的知识日渐生疏,推荐大家一本学习Java的好书,“达摩院”的《Java开发手册 华山版》,在阿里云只要0.01元,买了不吃亏,不怕垫桌角。




问题来了,看到这么一道题,“你会写一个单例吗?”  


太简单了,简直侮辱智商...


public class Singleton {

    private static Singleton uniqueSingleton;


    private Singleton() {

    }


    public Singleton getInstance() {

        if (null == uniqueSingleton) {

            uniqueSingleton = new Singleton();

        }

        return uniqueSingleton;

    }

}


等等... 既然这么问了,一定有陷阱,啊,多线程,来加一个锁


public class Singleton {

    private static Singleton uniqueSingleton;


    private Singleton() {

    }

    public synchronized Singleton getInstance() {

        if (null == uniqueSingleton) {

            uniqueSingleton = new Singleton();

        }

        return uniqueSingleton;

    }

}


这样性能好像有点问题,原本第一次才需要判断是否初始化,现在次次都要加锁, 敲豆麻袋... 引入 “双重检查锁”(double checked locking)吧


public class Singleton {

    private static Singleton uniqueSingleton;


    private Singleton() {

    }


    public Singleton getInstance() {

        if (null == uniqueSingleton) {

            synchronized (Singleton.class) {

                if (null == uniqueSingleton) {

                    uniqueSingleton = new Singleton(); 

                }

            }

        }

        return uniqueSingleton;

    }

}


这样是不是就完美了呢?答案 “并不是”


引入《开发手册》原文推荐


在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化 问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration),推荐解决方案中较为 简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型。


修改一下

public class Singleton {

    private volatile static Singleton uniqueSingleton;


    private Singleton() {

    }


    public Singleton getInstance() {

        if (null == uniqueSingleton) {

            synchronized (Singleton.class) {

                if (null == uniqueSingleton) {

                    uniqueSingleton = new Singleton();

                }

            }

        }

        return uniqueSingleton;

    }

}


使用了volatile关键字后,重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前。


参考资料:

https://www.cnblogs.com/xz816111/p/8470048.html


完美,睡觉了。