//懒汉 class LazySingleton{ private static LazySingleton singleton; private LazySingleton(){ } public static LazySingleton getInstance(){ synchronized(this){ if(singleton==null){ singleton=new LazySingleton(); } return singleton; } } } //恶汉 class HungrySingleton{ private static HungrySingleton singleton=new HungrySingleton(); private HungrySingleton(){} public static HungrySingleton getInstance(){ return singleton; } } //双重校验锁 class LockSingleton{ private volatile static LockSingleton singleton; private LockSingleton(){} public static LockSingleton getInstance(){ if(singleton==null){ synchronized(LockSingleton.class){ if(singleton==null){ singleton=new LockSingleton(); } } } return singleton; } } //静态内部类 public class Singleton { private Singleton(){ if(SigletonHolder.instance!=null){ throw new AssertionError(); } } private static class SigletonHolder { private static final Singleton instance = new Singleton(); } public static final Singleton getInstance() { return SigletonHolder.instance; } } //枚举 public enum Singleton { instance; // 定义一个枚举的元素,就代表Singleton的一个实例 public void doSomeThing(){ } }
总结:五类:懒汉,恶汉,双重校验锁,静态内部类,枚举。
恶汉:因为加载类的时候就创建实例,所以线程安全(多个ClassLoader存在时例外)。缺点是不能延时加载。
懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。
双重校验锁:麻烦,在当前Java内存模型中不一定都管用,某些平台和编译器甚至是错误的,因为instance = new MaYun()这种代码在不同编译器上的行为和实现方式不可预知。
静态内部类:延迟加载,减少内存开销。因为用到的时候才加载,避免了静态field在单例类加载时即进入到堆内存的permanent代而永远得不到回收的缺点(大多数垃圾回收算法是这样)。
枚举:很好,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载。
注:
双重校验所问题:JAVA内存模型一些原因偶尔会失败,内存模型允许所谓的“无序写入”,这是失败的一个主要原因,初始化实例的写入操作和实例字段的写入操作能够被编译器或者缓冲区重排序,重排序可能会导致返回部分构造的一些东西。在JDK1.5新的内存模型下,实例字段使用volatile可以解决双重锁检查的问题,因为在构造线程来初始化一些东西和读取线程返回它的值之间有happens-before关系。
防止反序列化:重写readResolve()接口,直接返回当前实例。
防止反射:私有构造器中添加非空逻辑判断和throw new AssertionError();