懒汉式-线程不安全
在以下代码中,Singleton的实例化被延迟执行,只在需要的时候进行初始化
但是也是线程不安全的,可能存在多个线程进入getUniqueInstance方法
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
懒汉式-线程安全
使用锁的方式,保证只有一个线程进入该方法。但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
双重校验锁模式-线程安全
双重校验先判断getUniqueInstance是否初始化,如果已经初始化则不用加锁,若没有初始化则对初始化代码进行加锁。
使用两层if的目的:如果有两个线程同时进入第一层判断,并且都成功往下走,虽然有同步锁,如果没有第二层判断,则都会进行一次实例化操作,只是先后问题。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行,否则其他线程可能会拿到一个还未初始化的实例。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
饿汉式-线程安全
该方法是线程安全的,但也丢失了延迟实例化带来的节约资源的好处。
private static Singleton uniqueInstance = new Singleton();
静态内部类实现
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举类实现
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
public enum Singleton {
uniqueInstance;
}