优点:绝对线程安全,实现简单
缺点:类加载时就实例化,如果单例数量众多,浪费内存。
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
懒汉单例
优点:延迟加载,只有使用的时候才实例化
缺点:加锁,影响性能
写法一(锁双重校验):
public class LazySingleton {
//volatile避免指令重排:实例化时3先执行,其它线程满足instance!=null,但使用实例的时候堆空间还未初始化就会报错
private volatile static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance != null) {
return instance;
}
synchronized (LazySingleton.class) {
//双重校验
if (instance != null) {
return instance;
}
//1开辟堆空间-》2堆空间初始化-》3引用指向空间地址
instance = new LazySingleton();
}
return instance;
}
}
写法二(静态内部类):
public class InnerClassSingleton {
private InnerClassSingleton() {
}
public InnerClassSingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
public final static InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
}
容器化单例
个人理解,和懒汉思想一样,只是通过map来保存实例而已。
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map map = new ConcurrentHashMap<>();
public static Object getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
if (map.containsKey(className)) {
return map.get(className);
}
synchronized (map) {
if (map.containsKey(className)) {
return map.get(className);
}
Object obj = Class.forName(className).newInstance();
map.put(className, obj);
return obj;
}
}
}
但以上单列都有一个共同的缺点,就是可以被反射(在构造函数中加逻辑判断可以防止反射破坏)和序列化破坏(增加readResolve()方法避免序列化破坏)。于是就有了枚举单列的出现。
readResolve原理:在反序列化的时候,如果存在readResolve方法,就会使用这个方法返回的实例,丢弃新生成的实例,缺点就是有新生成的实例,虽然丢弃了,但是还是浪费内存。
枚举单列优点:线程安全,可避免反射和序列化破坏单列,写法简单优雅
缺点:类加载时就实例化,和饿汉一样,大量单列的情况下对内存不友好。
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}



