版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons)
PS:第一次尝试发博客,如有不足之处,欢迎多多指点!
单例模式(Singleton)
- 核心作用
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见应用场景
- 项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件数据,每次new一个对象去读取
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
- spring中,每个bean默认就是单例的,这样做的优点是spring容器可以管理
- 在spring MVC框架/struts1框架中,控制器对象也是单例
- application 也是单例的典型应用
- 在servlet编程中,每个servlet也是单例
单例模式的优点
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
- 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
常见的五种单例模式实现方式
- 主要
-饿汉式(线程安全,调用效率高,但是不能延时加载)
-懒汉式(线程安全,调用效率不高,但是可以延时加载)
-其他
-双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
-静态内部类式(线程安全,调用效率高,可以延时加载)
-枚举单例(线程安全,调用效率高,不能延时加载)
如何选用?
- 单例对象 占用资源少,不需要延时加载
枚举式 好于 饿汉式 - 单例对象 占用资源大,需要延时加载
静态内部类式 好于 懒汉式
饿汉式(单例对象立即加载)
-
饿汉式单例模式代码中,static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题,虚拟机保证只会加载一次该类,肯定不会发生并发访问的问题,因此,可以省略synchronized关键字
-如果只是加载本类,而不是要调用getSingleton(),甚至永远没有调用,则会造成资源浪费class SingletonEHan{ //类初始化时,立即加载这个对象(没有延时加载的优势,)加载时,天然的是线程安全的 private static SingletonEHan singleton = new SingletonEHan(); //私有化构造器 private SingletonEHan() { } //方法不同步,调用效率高 public static SingletonEHan getSingleton() { return singleton; } }
懒汉式(单例对象延时加载)
-
lazy load 延时加载,懒加载,真正用的时候才加载
-资源利用率高,但是每次调用getSingleton()方法都要同步,并发效率较低class SingletonLHan{ //类初始化时,不初始化这个对象,延时加载,真正需要时再创建 private static SingletonLHan singleton; //私有构造器 private SingletonLHan() { } //方法同步,调用效率低 public static synchronized SingletonLHan getSingleton() { if (singleton == null){ singleton = new SingletonLHan(); } return singleton; } }
双重检测锁模式
-
这个模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了
-由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用class SingletonDouble{ private static SingletonDouble singleton; //私有构造器 private SingletonDouble(){} // public static SingletonDouble getSingleton(){ if (singleton == null){ SingletonDouble sc; synchronized (SingletonDouble.class){ sc = singleton; if (sc == null){ synchronized (SingletonDouble.class){ if (sc == null){ sc = new SingletonDouble(); } } singleton = sc; } } } return singleton; } }
静态内部类(也是一种懒加载)
-
外部类没有static属性,则不会像饿汉式那样立即加载对象
-
只有真正调用getSingleton(),才会加载静态内部类,加载类时是线程安全的,singleton是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
-
兼备了并发高效调用和延迟加载的优势
class SingletonStatic{ //创建一个静态内部类 private static class SingletonClass{ private static final SingletonStatic singleton = new SingletonStatic(); } //调用时,通过return 再创建 public static SingletonStatic getSingleton(){ return SingletonClass.singleton; } //私有构造器 private SingletonStatic(){} }
枚举单例模式
-
优点:实现简单,枚举本身就是单例模式,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞
-
缺点:无延时加载
enum SingletonEnum{ /** * 定义一个枚举的元素,它就代表了Singleton的一个实例 */ INSTANCE; /** * 单例可以有自己的操作 */ public void singletonOperation(){ //功能处理 } }