设计模式之单例模式
一、单例模式的特点:
-
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
二、常见的几种单例模式
1、饿汉式,静态常量
- 在类装载时就完成类的初始化,没有达到lazy loading的效果,如果从始至终没有使用过这个类,可能造成内存浪费。
- 避免了多线程同步问题
public class Singleton01 { private final static Singleton01 instance = new Singleton01(); //构造器私有化(防止new) private Singleton01() { } public static Singleton01 getInstance() { return instance; } }
2、饿汉式,静态代码块
- 将类实例化的过程放在了静态代码块中,也是在类装载的时候就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的
public class Singleton02 { private static Singleton02 instance; //在静态代码块中创建单例对象 static { instance = new Singleton02(); } private Singleton02() { } public static Singleton02 getInstance() { return instance; } }
3、懒汉式,线程不安全(不推荐使用)
-
只能在单线程下使用。
-
多线程下,一个线程进入了if(singleton==null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
public class Singleton03 { //声明类的唯一实例 private static Singleton03 instance; //造方法私有化,不允许外部直接创建对象 private Singleton03() { } //当使用到getInstance()方法时,才去创建该对象 public static Singleton03 getInstance() { if (instance == null) { instance = new Singleton03(); } return instance; } }
4、懒汉式,线程安全(不推荐使用)
- 问题:效率太低,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步
public class Singleton04 { //声明类的唯一实例 private static Singleton04 instance; //造方法私有化,不允许外部直接创建对象 private Singleton04() { } //当使用到getInstance()方法时,才去创建该对象 public synchronized static Singleton04 getInstance() { if (instance == null) { instance = new Singleton04(); } return instance; } }
5、DCL双重检锁机制(Double Check Lock)【推荐使用】
- 两次检查instance == null,一次是在同步块外,一次是在同步块内。第一次检查instance不为null,那么就不需要执行下面的加锁和初始化操作。可以大幅降低synchronized带来的性能开销。
- 使用volatile禁止指令重排序,对volatile变量的写操作都先行发生于后面对这个变量的读操作
class Singleton05 { private static volatile Singleton05 instance; private Singleton05() { System.out.println(Thread.currentThread().getName() + " 构造方法SingletonDemo"); } //解决线程安全问题,解决懒加载问题,同时保证了效率 public static Singleton05 getInstance() { if (instance == null) { synchronized (Singleton05.class) { if (instance == null) { instance = new Singleton05(); } } } return instance; } }
6、静态内部类(推荐使用)
- 采用了类装载的机制来保证初始化实例时只有一个线程。
- Singleton07类装载时,不会导致静态内部类被装载,当调用getInstance()方法时,静态内部类会装载,而且只装载一次,并且是线程安全的
- 避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
public class Singleton06 { private static class SingletonInstance { private static final Singleton06 INSTANCE = new Singleton06(); } //构造器私有化 private Singleton06() { } public static final Singleton06 getInstance() { return SingletonInstance.INSTANCE; } }
7、枚举(推荐使用)
- 不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
public enum Singleton07 { INSTANCE; }
总结:
一般情况下直接使用饿汉式就好了,
如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,
如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例
三、单例模式的使用场景:
需要频繁创建和销毁的对象
创建对象耗时过多或耗费资源过多,但又经常用到的对象
频繁访问数据库或文件的对象(比如Hibernate的SessionFactory)
四:实际应用
JDK中java.lang.Runtime 使用单例模式的饿汉式
基本翻译
n. 实例;情况;建议
vt. 举...为例