1. 单例模式介绍
单例模式提供了一种创建对象的最佳形式。
由某类负责创立本人的对象,并提供该对象的拜访形式,以确保该对象是该类惟一的实例。
(1) 实用状况:
存在一个全局应用的类被频繁的创立和销毁时,能够思考应用单例模式。
(2) 长处:
在堆中有且仅有一个实例,防止了频繁创立销毁以及保留的内存开销。
(3) 毛病:
没有接口,不能继承。
它在类的外部创立了本人的对象,这与繁多职责准则抵触,它原本不应该关怀内部是怎么实例化它的。
2. 单例模式实现
(1) 懒汉式(线程不平安)
先来看一种最简略的单例模式:
public class Singleton {
private static Singleton instance;
// 公有构造方法
private Singleton() {}
public static Singleton getSingleton() {if (instance == null) {instance = new Singleton();
}
return instance;
}
}
为什么叫懒汉,因为它不急着创立实例,而是等到断定为空的时候才去创立
它最大的问题就是线程不平安,因为可能会有多个线程断定 instance == null,从而创立多个实例,严格意义上说,它并不能算单例模式。
(2) 懒汉式(线程平安)
上边的实现是线程不平安的,那给 getSingleton 办法减少 synchronized 不就行了吗:
public class Singleton {
private static Singleton instance;
// 公有构造方法
private Singleton() {}
public static synchronized Singleton getSingleton() {if (instance == null) {instance = new Singleton();
}
return instance;
}
}
这样做的确是能够保障线程平安的,然而加锁会影响效率
其实除了第一次须要创立之外,后续的加锁就没有意义了
(3) 饿汉式
饿汉式指的是在类加载的时候,就间接创立了实例:
public class Singleton {private static Singleton instance = new Singleton();
// 公有构造方法
private Singleton() {}
public static Singleton getSingleton() {return instance;}
}
这是比拟罕用的形式,也能够保障线程平安
然而在类加载的时候就实例化的话,万一它很耗费资源,就有点节约内存
(4) 双重校验锁
应用双重校验锁的形式能同时保障线程平安和高性能:
public class Singleton {
// 留神这里的 volatile
private volatile static Singleton instance;
// 公有构造方法
private Singleton() {}
public static Singleton getSingleton() {
// 第一次判断是否为空,为空才创立,否则间接返回
if (instance == null) {synchronized (Singleton.class) {
// 第二次判断是否为空,避免已被其余线程实例化之后反复实例化
if (instance == null) {instance = new Singleton();
}
}
}
return instance;
}
}
这种形式中判断了两次 instance == null:
第一次判断是否为空,为空才创立,否则间接返回
第二次判断是否为空,避免已被其余线程实例化之后反复实例化
这里的要害是在申明 instance 变量的时候,减少了 volatile 关键字
volatile 有两个作用:
- 保障了不同线程对这个变量进行操作时的可见性,即一个线程批改了某个变量的值,这新值对其余线程来说是立刻可见的。但如果对该变量执行的是非原子操作,它不能保障线程平安
- 禁止指令重排序