什么是单列模式
保证一个类的实例是唯一的
单列模式的作用
- 节省内存,不需要对一个类进行多次实例化,从而节省内存
- 管理方便,比如Spring框架的IOC设计模式中冰山一角的思想一样,交给一个容器进行统一管理,减少类的滥用
- 保证数据唯一性,在某些程序流程执行的过程中需要的唯一性,例如:简单的Java实现的网站访问统计
单列模式的几种实现方式
- 懒汉模式,这是线程不安全的,这是延迟加载的意思
public class TwelveT {
private static TwelveT instance;
// 禁止手动进行实例,应交由本类处理实例
private TwelveT(){}
public static TwelveT getInstance() {
if (instance == null) {
instance = new TwelveT();
}
return instance;
}
}
- 懒汉模式,线程安全的,由于是线程安全的,所以效率相对来说会有所减低
public class TwelveT {
private static TwelveT instance;
private TwelveT(){}
// 加上synchronized使得它是线程安全的
public static synchronized TwelveT getInstance() {
if (instance == null) {
instance = new TwelveT();
}
return instance;
}
}
- DCL双锁检验模式,在这里提供一种解决可以通过反射破坏单列模式的一种方法,但并没有完全解决反射所带来的的问题,还有待解决
public class TwelveT {
private static boolean flag = false;
/**
* 私有构造器,不允许直接new
*/
private TwelveT() {
synchronized (TwelveT.class) {
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("不要试图通过反射破坏单例");
}
}
}
/**
* 类初始化的时候,立即加载该对象
* 1. 保证可见性(多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值)
* 2. 不保证原子性(操作是可以中断,所以在并发中可能会出现一些问题)
* 3. 禁止指令重排(禁止编译器和CPU可能的对执行指令重新排序带来的问题)
*/
private volatile static TwelveT instance;
/**
* 提供获取对象的方法,没有synchronized,效率高
* 使用synchronized代码块进行双重判断,效率比直接synchronized性能高
*
* @return
*/
public static TwelveT getInstance() {
if (instance == null) {
synchronized (TwelveT.class) {
if (instance == null) {
instance = new TwelveT();
}
}
}
return instance;
}
}
// 以下代码示例一下反射破坏单列,明显执行失败的
class SingletonTest{
public static void main(String[] args) throws Exception {
// 正常获取实例
TwelveT instance = TwelveT.getInstance();
Constructor<TwelveT> declaredConstructor = TwelveT.class.getDeclaredConstructor();
// 允许通过反射访问受保护的资源
declaredConstructor.setAccessible(true);
// 获取实例,直接抛出错误
TwelveT instance02 = declaredConstructor.newInstance();
System.out.println(instance == instance02);
}
}
- 饿汉式,线程不安全的方式,在Java当中这种设计模式在程序启动的时候就会被立即加载,也就是没有使用我们的延迟加载,一但这种设计写多了,程序的启动将会非常缓慢,慎用
public class TwelveT {
private static TwelveT instance = new TwelveT();
private TwelveT(){}
public static TwelveT getInstance() {
return instance;
}
}
- 枚举
public enum TwelveT {
INSTANCE;
public void whateverMethod() {
}
}
- 登记式(内部类实现的懒汉式),内部类只有在外部类被调用才加载,而且不用加锁,此模式有懒汉式以及饿汉的优点,屏蔽了他们的一些缺点,可以说是最好的单例模式。
public class TwelveT {
private static class TwelveTHolder {
private static final TwelveT INSTANCE = new TwelveT();
}
private TwelveT() {
}
public static final TwelveT getInstance() {
return TwelveTHolder.INSTANCE;
}
}