单例与线程安全

来源:互联网 发布:复旦大学大数据试验场 编辑:程序博客网 时间:2024/06/05 10:45

说到单例,很容易就想到了饿汉式的单例

public class Singleton {// 实例化private static final Singleton INSTANCE = new Singleton();/** * 构造私有 */private Singleton() {}/** *  * @author wiggin * @Description: 获取实例 * @return * */public static Singleton getInstance() {return INSTANCE;}}

这种写法的好处在于它是线程安全的,确保了在多线程情况下,程序内部只有唯一一个实例。

当然他的缺点也是明显的:在类加载的时候,就已经进行实例化,无论之后用不用到。如果该类比较占内存,之后又没用到,就白白浪费了资源。

这个时候就衍生出另外一种单例的写法:实例在需要用的的时候再进行创建,其实也就是为了实现懒加载,这时候就有了下面这种写法

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

这种写法确实实现了懒加载,但是却有一个致命的问题:线程不安全,在多线程访问的情况下,有可能实例化多个实例。

那么为了实现线程安全,就有了下面的写法

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


现在是线程安全,但是因为同步范围过大,且大多数情况下并不需要同步,在多线程的情况下,其性能很差。

为了兼顾性能,那就把同步的范围缩小吧,只在实例化的时候加锁,这就有了Double-check写法

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



这样子看似完美的解决了之前碰到的问题,但是遗憾的是,这样写依旧是有问题的:jvm指令重排序(正常代码执行顺序应该是1234,但是jvm进行指令重排后,可能是1324),这也使得上面的程序不是绝对的线程安全。此时,就有了下面这种写法

public class Singleton {// volatile关键字private static volatile Singleton INSTANCE = null;public Singleton() {}public static  Singleton getInstance(){if (INSTANCE == null) {synchronized (INSTANCE) {if (INSTANCE == null) {INSTANCE = new Singleton();}}}return INSTANCE;}}

引入了volatile关键字,保证了懒加载以及线程安全






原创粉丝点击