首页 > 代码库 > 单例模式—层层剖析寻找最高效安全的单例
单例模式—层层剖析寻找最高效安全的单例
问题来源
什么是单例?它的运用场景是什么?
单例模式是指保证在系统中只存在某类唯一对象。运用场景随处可见,例如工具类、Spring容器默认new对象等。
单例模式有几种实现方式?
饿汉式、懒汉式、双重检查锁式、内部类式。
推荐使用方式?
饿汉式、内部类式。
饿汉式
饿汉式顾名思义饿,那么当应用程序一开始类加载,类的对象立马实例化加载至JVM。
1 public class SingletonClass { 2 /** 3 * 优点:调用效率高。 4 * 缺点:没有延迟加载。 5 */ 6 private static SingletonClass instance =new SingletonClass(); 7 8 public static SingletonClass getInstance(){ 9 return instance;10 }11 }
为什么调用效率高?没有延迟加载?
答:假设在高并发的场景下,有10W+并发调用,不需要同步处理。可以直接在堆内存直接获取对象不需要任何等待。
同样,它没有延迟加载,如果它是需要消耗很大内存的对象,最开始就加载入堆内存,而用户暂时不需要。这样就会严重占用堆内存,影响运行效率。
懒汉式
导引:脑洞大开的程序员们说:上述问题还不简单,当调用的时候在new对象不就行。于是出现了懒汉式的雏形版本。
public class SingletonClass { private static SingletonClass instance; public static SingletonClass getInstance(){ if(null==instance){ instance=new SingletonClass(); } return instance; }}
懒汉式顾名思义懒,就是延迟加载,当被调用的时候再实例化。
问题:如果你是初出茅庐的应届生写成这样,估计面试官也不会追究什么。如果你是有一年工作年限的程序员,估计面试官就会声讨你了。假设,并发数10W+,它就将被蹂躏的不堪入目。那么我们需要怎么解决呢?加上同步操作就大功告成。
1 public class SingletonClass { 2 3 //调用效率低、延迟加载 4 private static SingletonClass instance; 5 6 public static synchronized SingletonClass getInstance(){ 7 if(null==instance){ 8 instance=new SingletonClass(); 9 }10 return instance;11 }12 }
问题:从效率维度考虑,估计这样已经完美了吧?但是,从安全纬度考虑,依然隐隐约约存在问题。如果是接触过反射、反序列化的同学,我们一起来继续探讨。
/** * 通过反射破坏懒汉式单例 * @author aaron */public class Client { public static void main(String[] args) throws Exception { SingletonClass clazzOne=SingletonClass.getInstance(); SingletonClass clazzTwo=SingletonClass.getInstance(); System.out.println("clazzOne-hasCode:"+clazzOne.hashCode()); System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode()); Class<SingletonClass> clazz=(Class<SingletonClass>)Class.forName("singleton.SingletonClass"); Constructor<SingletonClass> c=clazz.getConstructor(null); c.setAccessible(true); SingletonClass clazzThree=c.newInstance(); SingletonClass clazzFour=c.newInstance(); System.out.println("clazzThree-hasCode:"+clazzThree.hashCode()); System.out.println("clazzFour-hasCode:"+clazzFour.hashCode()); }}
1 public class SingletonClass implements Serializable{ 2 3 private static SingletonClass instance; 4 5 public static synchronized SingletonClass getInstance(){ 6 if(null==instance){ 7 instance=new SingletonClass(); 8 } 9 return instance;10 }11 12 public static void main(String[] args) throws Exception {13 SingletonClass clazzOne=SingletonClass.getInstance();14 SingletonClass clazzTwo=SingletonClass.getInstance(); 15 System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());16 System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());17 18 19 FileOutputStream fos=new FileOutputStream(new File("f:/test.txt"));20 ObjectOutputStream bos=new ObjectOutputStream(fos);21 bos.writeObject(clazzOne);22 bos.close();23 fos.close();24 25 FileInputStream fis=new FileInputStream(new File("f:/test.txt"));26 ObjectInputStream bis=new ObjectInputStream(fis);27 SingletonClass clazzThree=(SingletonClass) bis.readObject();28 System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());29 }30 }
问题:这么轻易就被破解了?那怎么解决呢?
public class SingletonClass implements Serializable{ private static SingletonClass instance; private SingletonClass(){ //防止被反射 if(null!=instance){ throw new RuntimeException(); } } public static synchronized SingletonClass getInstance(){ if(null==instance){ instance=new SingletonClass(); } return instance; } //当没有定义这方法时,反序列化默认是重新new对象。 //反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象! private Object readResolve() throws ObjectStreamException{ return instance; }}
双重检查锁与内部类
双重检查锁与内部类的方式:缘由懒汉式、饿汉式要么存在调用效率低或者运行效率低问题。而这两种方式取前两者的优点为自己所用。
1 /** 2 * 单例模式-双重检查锁 3 * @author aaron 4 */ 5 public class SingletonClass{ 6 private static SingletonClass instance; 7 8 public static SingletonClass getInstance(){ 9 if(null==instance){10 synchronized (SingletonClass.class) {11 if(instance==null){12 instance=new SingletonClass();13 }14 }15 }16 return instance;17 }18 }
问题:缘由JVM对于此种方式的同步控制,并不稳定,当高并发的时候,可能会出现问题,并不推荐使用这种方式。理论上来说,它是不存在问题的。
1 /** 2 * 单例模式-内部类的方式 3 * @author aaron 4 */ 5 public class SingletonClass{ 6 7 private static class InnerClass{ 8 public static SingletonClass instance=new SingletonClass(); 9 }10 11 public static SingletonClass getInstance(){12 return InnerClass.instance;13 }14 }
版权声明
作者:xiaoyongAaron(邱勇)
出处:http://www.cnblogs.com/qiuyong/
您的支持是对博主深入思考总结的最大鼓励。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,尊重作者的劳动成果。
单例模式—层层剖析寻找最高效安全的单例