解决的问题
- 有些数据在系统中只应该保存一份,比如系统的配置信息类
- 资源访问冲突的问题,比如多个logger写入同一个日志文件
几种实现方式
饿汉式
- 静态成员变量,类加载时实例化
- 线程安全
- 不支持延迟加载
public class HungryManDemo {
private static final HungryManDemo instance = new HungryManDemo();
private HungryManDemo() {}
public static HungryManDemo getInstance(){
return instance;
}
}
懒汉式
- 支持延迟加载
- 不支持高并发
public class LazyManDemo {
private static LazyManDemo instance;
private LazyManDemo() {}
public static LazyManDemo getInstance() {
if (instance == null) {
instance = new LazyManDemo();
}
return instance;
}
}
懒汉式——双重检查
- 支持高并发
- 支持延迟加载
- 使用
volatile
,禁止指令重排序 - 在双重检测时可使用局部变量优化,减少访问
volatile
修饰的变量,以提升性能 - 实现复杂
public class LazyManDoubleCheckDemo {
private static volatile LazyManDoubleCheckDemo instance;
private LazyManDoubleCheckDemo() {}
public static LazyManDoubleCheckDemo getInstance() {
LazyManDoubleCheckDemo temp = instance;
if (temp == null) {
synchronized (LazyManDoubleCheckDemo.class) {
temp = instance;
if (temp == null) {
temp = new LazyManDoubleCheckDemo();
instance = temp;
}
}
}
return instance;
}
}
静态内部类
- 支持延迟加载
- 支持高并发
- 实现比双重检测简单
- SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。 由于SingletonClassInstance是私有静态内部类,所以不会被其他类知道,同样,static语义也要求不会有多个实例存在。并且,JSL规范定义,类的构造必须是原子性的,非并发的,因此不需要加同步块。同样,由于这个构造是并发的,所以getInstance()也并不需要加同步。
public class StaticInnerClassDemo {
private StaticInnerClassDemo() {}
private static class SingletonHolder{
private static final StaticInnerClassDemo instance = new StaticInnerClassDemo();
}
public static StaticInnerClassDemo getInstance() {
return SingletonHolder.instance;
}
}
枚举
- 不支持延迟加载
- 支持高并发
- 实现最简单
- 使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》作者推荐使用的方法。
public enum EnumDemo {
INSTANCE;
}
序列化问题
反序列化会产生新对象,违反单例规则
解决方案:JVM从内存中反序列化地"组装"一个新对象时,会自动调用类的readResolve方法,我们可以通过此方法返回指定好的对象。
代码语言:javascript复制public class SerialProblem {
private static volatile SerialProblem instance;
private SerialProblem() {}
public static SerialProblem getInstance() {
SerialProblem temp = instance;
if (temp == null) {
synchronized (SerialProblem.class) {
temp = instance;
if (temp == null) {
temp = new SerialProblem();
instance = temp;
}
}
}
return instance;
}
private Object readResolve() {
return instance;
}
}
反射问题
反射获取构造器,进行实例化产生新对象
解决方案:第二次实例化的时候,抛出异常
代码语言:javascript复制public class ReflectProblem {
private static volatile ReflectProblem instance;
private ReflectProblem() {
Assert.isNull(instance);
}
public static ReflectProblem getInstance() {
ReflectProblem temp = instance;
if (temp == null) {
synchronized (ReflectProblem.class) {
temp = instance;
if (temp == null) {
temp = new ReflectProblem();
instance = temp;
}
}
}
return instance;
}
}