一、单例模式的特点:
-
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
二、常见的几种单例模式
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. 举...为例