#什么是单例模式
单例模式是使用的最广泛的设计模式之一,用来确保一个类对外只提供唯一的实例,比如连接池、缓存、日志对象的实现基本都用的单例模式。实现单例的方法主要有以下几种,每种都有各自的优缺点,为了实现真正的单例,我们可以根据实际需求,选择合适的方法。
##1. 饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优点: 简单,线程安全
缺点: 不能延时加载
推荐指数: 4星
说明: 类初始化时就加载静态变量,创建了实例,不存在多线程问题,缺点是不能在实际使用的时候才创建对象
##2. 懒汉模式
- 线程不安全
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点: 延迟加载
缺点:线程不安全
推荐指数: 2星
说明:在多线程环境下,很可能产生多个实例。
- 线程安全
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点: 延迟加载,线程安全
缺点:性能较低
推荐指数: 3星
说明:保证了线程安全,但是由于是直接对方法进行同步,会降低性能,实际上只有在初次调用此方法时,才需要加锁来确保线程安全,一旦设置好instance实例之后,就不需再同步该方法了,也就是说,除了第一次,以后每次调用该方法,同步都是多余的
##3. 双重检查锁(DCL)
public class Singleton {
private volatile static Singleton instance;//volatile可防止指令重排
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优点: 延迟加载,线程安全,性能较高
缺点:较为繁琐,不适用于jdk1.5以下的版本
推荐指数: 4星
说明:该方法是对懒汉模式的改进,既保证了线程安全,又不是对整个方法进行同步,对性能基本没有什么影响。不足之处是需要使用volatile关键字来保证有序性,在创建对象时有三个步骤:
1、分配内存空间。
2、初始化对象。
3、将内存空间的地址赋值给对应的引用。
如果指令重排,导致实际创建对象的顺序变成
1、分配内存空间。
2、将内存空间的地址赋值给对应的引用。
3、初始化对象。
那么就有可能暴露一个未初始化的不可靠对象,而jdk1.5上的版本可以使用volatile关键字有效避免指令重排的问题,从而保证有序性。具体可以参考这篇文章:Java 并发编程:volatile的使用及其原理
##4. 枚举
public enum Singleton {
INSTANCE;
public void doSomething() {}
}
优点:代码简洁, 线程安全
缺点:非延迟加载
推荐指数: 5星
说明:通过枚举方式来创建单例是《Effective Java》作者Joshua Bloch大力推荐的,因为它有以下几点优势:
- 无法通过反射调用创建实例,而在其他类型中,即使是私有方法,也能通过反射,创建新的实例。查看 java.lang.reflect.Constructor的newInstance()方法中有如下代码,说明枚举类型是禁止了通过反射构造枚举对象的
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
2.可以有效的防止反序列化,以下是java.lang.Enum类的代码,可以看出,enum类型无法被反序列化
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
3 枚举实例无法被克隆,以下是java.lang.Enum代码,如果要强行进行clone,直接抛出异常
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
##5. 静态内部类
public class Singleton{
private Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static Singleton INSTANCE = new Singleton();
}
}
优点: 线程安全,延迟加载
缺点:
推荐指数: 5星
说明:该方式也称为IoDH( Initialization Demand Holder),可以说是结合了饿汉模式和懒汉模式的优点,既实现了延迟加载,也保证了线程安全,建议优先采用该方式创建单例。
参考: