保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通过定义静态的成员变量,以保证单例对象可以在类初始化的过程中被实例化。 这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。 所以, 除非被重写,这个方法默认在整个装载过程中都是线程安全的。所以在类加载过程中对象的创建也是线程安全的。
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }双重校验锁方式实现单例。
为什么用volatile修饰?
因为编译器有可能进行指令重排优化,使得singleton对象再未完成初始化之前就对其进行了赋值,这样其他人拿到的对象就可能是个残缺的对象了。使用volatile的目的是避免指令重排。保证先进性初始化,然后进行赋值
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }枚举底层依赖Enum类实现的,这个类的成员变量都是static类型的,并且在静态代码块中实例化,保证线程安全。
枚举可以解决反序列化破坏单例的问题。
在枚举序列化的时候,Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。 但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。
public enum Singleton { INSTANCE; public void whateverMethod() { } }解决方式:在Singleton的构造函数中增加如下代码
private Singleton() { if (singleton != null) { throw new RuntimeException("Singleton constructor is called... "); } }通过先序列化再反序列化的方式,可获取到一个新的单例对象,这就破坏了单例。
private void distoryBySerialize() { Singleton singleton = Singleton.getSingleton(); //Write Obj to file ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("tempFile")); oos.writeObject(singleton); //Read Obj from file File file = new File("tempFile"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); Singleton singletonBySerialize = (Singleton)ois.readObject(); //判断是否是同一个对象 System.out.println("singleton : " + singleton); System.out.println("singletonBySerialize : " + singletonBySerialize); System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize)); } catch (Exception e) { e.printStackTrace(); } }解决方式:在Singleton中增加readResolve方法,并在该方法中指定要返回的对象的生成策略几可以了。即序列化在Singleton类中增加以下代码即可:
private Object readResolve() { return getSingleton(); }