1.定义
单例模式是一种创建型的设计模式。单例模式意在保证一个类仅有一个实例,并且提供一个全局的访问点。
2.适用性
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时
一个国家只有一个主席/总统,一个人只有一个名字/身份证号,这些都是单例的例子。稍微实际点的例子就是,在某个服务器程序中,将服务器的配置信息存放在一个文件中,这些配置数据有一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
3.结构
参与者:
Singleton
—— 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作
—— 可能负责创建它自己的唯一实例
4.举例
我们先来实现一个简单的单例:
package com.andy.designpattern.singleton;
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
我们将构造函数设为private类型,使得客户无法构造多余的实例;instance设为类的类变量,保证类只有一个。乍看之下,这样的实现已经很完美了,但是这个方案在遇到多线程的时候还是会出问题:几个线程同时请求getInstance,可能多个线程同时通过if(instance == null)的检查,这样就可能产生多个实例。所以我们需要对上面的例子进行改进。
第一个中改进方案是在类装载的时候构建静态单例,请求get的时候直接返回:
package com.andy.designpattern.singleton;
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
第二种改进方案是通过synchronized、volatile等关键字将原始方案改写为线程安全的方案(此方案只适用于JDK5之后的版本,之前的版本使用“双重检查锁”会发生非预期行为):
package com.andy.designpattern.singleton;
public class Singleton {
private static volatile Singleton INSTANCE = null;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
这里为什么需要检查两次INSTANCE == null呢?
- 第一次检查if(INSTANCE == null)是判断INSTANCE是否被创建,如果已创建,就直接返回;否则开始线程同步
- 第二次检查if(INSTANCE == null)是判断在同步的线程当中是否有线程已经创建了INSTANCE,若有,则返回;否则创建并返回。
把INSTANCE声明为volatile有两个作用:
- 这个变量不会再多个线程中存在复本,直接从内存中读取
- 这个关键字会禁止指令重排序优化。
5.总结
其实单例模式还是比较简单的,就是说一个类只能有一个实例,于是我们把它声明为一个类的类变量,需要使用时判断是否存在,若存在即返回;若不存在,则调用私有构造器构造并返回。
6.参考文献