关于单例模式

it2022-05-05  115

一、高并发下的单例模式

1.1 双判空,代码如下:

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

方法getInstance中两次判空,内层的判空为了防止静态的单例对象两多次实例化,synchronized 关键字防止对多个线程同时实例化对象。因为synchronized会涉及到用户态和内核态的切换,非常耗时,如果每次调用getInstance方法都需要进行用户态和内核态的切换会严重影响性能,因此在最外层判空,当单例对象为空时才加锁进行实例化。

1.2 在内部类中实例化对象,代码如下:

public class Singleton{ private Singleton () {} private static class SingletonHandler{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonHandler.instance; } public void dosomething(){...} }

之所以采用内部类,而不是直接在单例类中直接实例化静态成员变量,如下:

public class Singleton{ private Singleton () {} ... private static Singleton instance = new Singleton(); public void dosomething(){...} ... }

有两个原因,第一个原因是延迟加载可以节约内存,直接实例化静态成员变量做不到这一点。第二点,当类不只是向外提供单例对象,还有其他功能时,例如上述代码中的dosomething方法,调用该方法需要实例化对象。若创建单例模式是一个很复杂的过程,那实例化对象就是一个非常耗时的过程,因为JVM会在首次实例化对象时加载单例模式,因此在调用dosomething方法前会存在不必要的等待,效率低下。而采用内不类的方式,只有再调用getInstance时,单例对象才会被实例化。

二、序列化对单例模式的影响

序列化是java非常重要的功能,当Singleton类继承了Serializable接口后。但是在反序列化时,java会调用readObject 方法通过反射机制调用无参构造器来新实例化一个Singleton对象,这样破坏了单例模式。为了防止单例模式在序列化的时候被破坏需要在Singleton类中定义私有的readResolve方法,供反射机制调用,原理可以参考https://www.hollischuang.com/archives/1144,代码如下:

public class Singleton{ private Singleton () {} private static class SingletonHandler{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonHandler.instance; } public void dosomething(){...} private Object readResolve() { return singleton; } }

 


最新回复(0)