单例模式
核心作用:
保证一个类只有一个对象,并且提供一个访问该实例的全局访问点。
常见应用场景:
1、windows中的任务管理器就是典型的单例模式
2、windows回收站也是单例模式
3、项目中读取配置文件的类也是单例模式
4、日志管理
。。。
单例模式的优点:
1、由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其它依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
2、单例模式可以设置全局访问点,优化共享资源的访问,例如可以设计一个单例类,负责所有数据表的映射处理
常见的五种单例模式实现方式:
主要:
饿汉式:(线程安全,调用效率高。但是不能延时加载)
懒汉式:(线程安全,调用效率不高。但是可以延时加载)
其他:
双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
静态内部类式(线程安全、调用效率高。但是可以延时加载)
枚举单例(线程安全、调用效率高。不能延时加载)
饿汉式
package com.pattern.singleton; /** * 测试饿汉单例模式 */ public class SingLetonDemo1 { //类初始化立即加载,所以叫饿汉式 //类初始化时,立即加载这个对象(不能延时加载),加载类时,天然的是线程安全的 private final static SingLetonDemo1 singLetonDemo1 = new SingLetonDemo1(); private SingLetonDemo1() { } //方法没有同步,调用效率高 public static SingLetonDemo1 getInstance() { return singLetonDemo1; } }
懒汉式
package com.pattern.singleton; /** * 测试懒汉单例模式 */ public class SingLetonDemo2 { //类初始化时,不初始化这个对象(延时加载,真正用的时候加载) private static SingLetonDemo2 singLetonDemo2 = null; private SingLetonDemo2() { } //方法同步调用效率低 public static synchronized SingLetonDemo2 getInstance() { if(singLetonDemo2 == null) { singLetonDemo2 = new SingLetonDemo2(); } return singLetonDemo2; } }
双重检测锁模式
这个模式将同步内容放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才进行同步创建了以后就没必要了。
package com.sjms.singleton; public class SingletonDemo3 { public static SingletonDemo3 instance = null; public static SingletonDemo3 getInstance() { if(instance == null) { SingletonDemo3 sc; synchronized (SingletonDemo3.class) { sc = instance; if(sc == null) { synchronized (SingletonDemo3.class) { sc = new SingletonDemo3(); } } instance = sc; } } return instance; } }
问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用。
静态类实现方式(也是一种懒汉加载方式)
package com.sjms.singleton; public class SingletonDemo4 { private SingletonDemo4() { } private static class SingletonClassInstance{ public static final SingletonDemo4 instance = new SingletonDemo4(); } public static SingletonDemo4 getInstance() { return SingletonClassInstance.instance; } }
要点:
外部类没有static属性,则不会像恶汉式那样立即加载对象
只有真正调用getInsatnce函数的时候,才会加载静态内部类。加载类时,线程是安全的。instance是static final 类型,保证了内存中只有这样一个类型实例存在,从而保证了线程的安全性
兼并了并发高效调用和延时加载的优势。
枚举实现方式:
public enum SingletonDemo5 { instace; public static void doSomething() { } }
优点:实现简单
枚举本身就是单例模式。由jvm从根本上提供保障!避免通过反射和反序列化的漏洞~
缺点:无延迟加载
总结:
主要:饿汉式 (线程安全,调用效率高,但是不能延时加载)
懒汉式 (线程安全,调用效率不高,但是能延时加载)
其它:双重检测锁式(由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用)
静态内部类式(线程安全,调用效率高,可以延时加载)
枚举式(线程安全,调用效率高,不可以延时加载)
如何选用:
单例对象占用资源少,不需要演示加载 (枚举式好于饿汉式)
单例对象占用资源大,需要延时加载(静态内部类好于懒汉式)