单例模式的优点:
——由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需 要比较多的资源时,如读取 配置、产生其他依赖对象时,可以通过在应用启动时直接产生一个单例对象,然后永久留内存的方式来解决。
——单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计 一个单例类,负责所有数据表 的映射处理。
常见的五种单例模式实现方式:
——主要:
饿汉式(线程安全,调用效率高。但是不能延时加载)
懒汉式(线程安全,调用效率不高,但是可以延时加载)
——其他:
双重检测锁式:(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
静态内部类式:(线程安全,调用效率高。可以延时加载)
枚举单例:(天然单例,唯一遗憾就是不能延时加载)
———————————————————————————————————————————————
饿汉式实现(单例对象立即加载)
饿汉式单例模式代码中,static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
懒汉式实现:(单例对象延时加载)
延时加载、懒加载!真正用的时候才加载!
问题:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率低。
双重检测锁实现:
这个模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。
问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用。
静态内部类实现方式(也是一种懒加载方式)
外部类没有static属性,则不会像饿汉式那样立即加载对象。
只有真正调用getInstance()才会加载静态内部类。加载类时是线程安全的。instance是是static final类型,保证了内存中只有一个这样的实例存在,而且只能被赋值一次,从而保证了线程安全性。
兼备了并发高效调用和延时加载的优势!
使用枚举类实现单例(天然单例)
优点:实现简单。枚举本身就是单例模式由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
缺点:无延时加载
单例模式问题:
反射可以破解上面几种(不包含枚举)实现方式!
解决:可以在构造方法中手动抛出异常控制
反序列化可以破解上面几种(不包含枚举)实现方式!
解决:可以通过定义readResovle()防止获得不同对象
如何选用?
单例对象占用资源少,不需要延时加载:枚举式好于饿汉式
单例对象占用资源大,需要延时加载:静态内部类好于懒汉式