定义
在一个JVM实例内存中有且只有一个类的实例。
实现方式
基于类的实现
public final class ClassSingleton {
private static ClassSingleton INSTANCE;
private String info = "Initial info class";
private ClassSingleton() {
}
public static ClassSingleton getInstance() {
if(INSTANCE == null) {
INSTANCE = new ClassSingleton();
}
return INSTANCE;
}
// getters and setters
}
这是比较常见的一种实现方式,但是此方式在多线程调用下会有问题,假如两个线程同时执行 getInstance()方法中
if(INSTANCE == null) ,两个线程各自获得类ClassSingleton不同的实例。为了解决并发创建多实例问题,我们需要在 getInstance()
方法上加锁,实现如下:
public static synchronized ClassSingleton getInstance(){}
直接在方法上加锁,多线程调用会降低性能,是否可以把加锁的代码范围缩小呢?可以双检查机制实现,
public final class ClassSingleton {
private static volatile ClassSingleton INSTANCE;
private String info = "Initial info class";
private ClassSingleton() {
}
public static ClassSingleton getInstance() {
if(INSTANCE == null) {
synchronized(ClassSingleton.class){
if(INSTANCE == null) {
INSTANCE = new ClassSingleton();
}
}
}
return INSTANCE;
}
// getters and setters
}
上面代码需要添加 volatile 保证线程安全性,为何要加,请见The “Double-Checked Locking is Broken” Declaration
如何在不加锁的情况下,实现方法的线程安全性呢? 看下下面的一种实现方法:
public final class ClassSingleton {
private static ClassSingleton INSTANCE = new ClassSingleton();
private String info = "Initial info class";
private ClassSingleton() {
}
public static ClassSingleton getInstance() {
return INSTANCE;
}
// getters and setters
}
此方式利用了java 语言规范:类静态字段和块的初始化保障线程安全性 Java Language Specification 12.4.2
上面实现方式我们一般称为:饿汉式,那如何利用此语言特性实现延迟初始化实例?
public final class ClassSingleton {
private static class ClassSingletonHolder{
private static final ClassSingleton INSTANCE = new ClassSingleton();
}
private ClassSingleton() {
}
public static ClassSingleton getInstance() {
return ClassSingletonHolder.INSTANCE;
}
// getters and setters
}
基于枚举的单例
public enum EnumSingleton {
INSTANCE("Initial class info");
private String info;
private EnumSingleton(String info) {
this.info = info;
}
public EnumSingleton getInstance() {
return INSTANCE;
}
// getters and setters
}
利用 Enum 特性,实现的单例可以保证线程安全,并可以被安全的序列化。