原文连接:http://blog.csdn.net/wzgiceman/article/details/51809985
单例的实现方式
Java单例模式可能是最简单也是最常用的设计模式,一个完美的单例需要做到哪些事呢?
- 线程安全
- 性能
- 防止序列化产生新对象
- 防止反射攻击
方式一:
public enum Singleton{ INSTANCE; }
优势:
- 利用的枚举的特性实现单例
- 由JVM保证线程安全
- 序列化和反射攻击已经被枚举解决
方式二:
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } }
实现Serializable接口需要重写
readResolve方法并return INSTANCE
,才能保证其反序列化依旧是单例
方式三:
public class Singleton { private volatile static Singleton INSTANCE; private Singleton (){} public static Singleton getSingleton() { if (INSTANCE == null) { synchronized (Singleton.class) { if (INSTANCE == null) { INSTANCE = new Singleton(); } } } return INSTANCE; } }
使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。比如上面的例子,取操作必须在执行完 1-2-3 之后或者 1-3-2 之后,不存在执行到 1-3 然后取到值的情况。从「先行发生原则」的角度理解的话,就是对于一个 volatile 变量的写操作都先行发生于后面对这个变量的读操作(这里的“后面”是时间上的先后顺序)。
但是特别注意在 Java 5 以前的版本使用了 volatile 的双检锁还是有问题的。其原因是 Java 5 以前的 JMM (Java 内存模型)是存在缺陷的,即时将变量声明成 volatile 也不能完全避免重排序,主要是 volatile 变量前后的代码仍然存在重排序问题。这个 volatile 屏蔽重排序的问题在 Java 5 中才得以修复,所以在这之后才可以放心使用 volatile。
方式四:
public class Singleton { private static class SingletonHolder{ private static final Singleton instance = new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return SingletonHolder.instance; } }
使用内部类来维护单例的实例,当Singleton被加载时,其内部类并不会被初始化,故可以确保当 Singleton类被载入JVM时,不会初始化单例类。只有 getInstance()
方法调用时,才会初始化 instance。同时,由于实例的建立是时在类加载时完成,故天生对多线程友好,getInstance()
方法也无需使用同步关键字。