解析每种实现的线程安全,以及单例模式相关面试题的解答。给出破坏单例模式的方法及解决办法
介绍
单例模式有以下特点:
1、单例类只能有一个实例。(构造器私有)
2、单例类必须自己创建自己的唯一实例。(懒汉式懒加载,饿汉式直接创建)
3、单例类必须给所有其他对象提供这一实例。(getInstance())
实现
普通饿汉式
//1.为什么加final?===防止子类继承从而重写方法改写单例
final class SingleDemo1 {
//2.这样初始化是否能保证单例对象创建时的线程安全?===可以,由类加载器保障
private static final SingleDemo1 INSTANCE=new SingleDemo1();
//3.为什么设置为私有?是否能防止反射创建新的实例?===防止外部调用构造器创建多个实例。不能防止反射创建新实例。
private SingleDemo1(){}
//4.为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由===提供更好的封装性,可以在方法内部实现一些懒惰的初始化逻辑。
public static SingleDemo1 getInstance(){
return INSTANCE;
}
}
枚举饿汉式(推荐)
//1.枚举单例是如何限制实例个数的?===按照声明的个数在类加载时实例化对象
//2.枚举单例在创建时是否有并发问题?===没有,由类加载器保障安全性
//3.枚举单例能否被反射破坏单例?===不能
//4.枚举单例能否被反序列化破坏单例?===不能
//5.枚举单例属于懒汉式还是饿汉式?===饿汉
//6.枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做?===写构造方法
final class SingleEnum{
private SingleEnum(){}
public static final SingleEnum getInstance(){
return SingleEnumDemo.INSTANCE.getInstance();
}
private enum SingleEnumDemo{
INSTANCE;
private final SingleEnum singleEnum;
SingleEnumDemo(){
singleEnum=new SingleEnum();
}
public final SingleEnum getInstance(){
return singleEnum;
}
}
}
普通懒汉式
final class SingleDemo3{
private static SingleDemo3 INSTANCE=null;
private SingleDemo3(){}
//1.分析这里的线程安全, 并说明有什么缺点?===线程安全,同步代码块范围过大,性能较差
public static synchronized SingleDemo3 getInstance(){
if (INSTANCE==null){
INSTANCE=new SingleDemo3();
}
return INSTANCE;
}
}
DCL懒汉式
final class SingleDemo4{
//1.解释为什么要加 volatile ?===防止指令重排导致异常
private static volatile SingleDemo4 INSTANCE=null;
private SingleDemo4(){
}
//2.对比实现3:SingleDemo3, 说出这样做的意义?=== 缩小同步范围,提高性能
public static SingleDemo4 getInstance(){
if (INSTANCE!=null){
return INSTANCE;
}
synchronized (SingleDemo4.class){
//3.为什么还要在这里加为空判断, 之前不是判断过了吗?===可能存在多个线程进入为空逻辑
if (INSTANCE==null){
INSTANCE=new SingleDemo4();
}
return INSTANCE;
}
}
}
静态内部类懒汉式
final class SingleDemo5{
private SingleDemo5(){}
//1.在创建时是否有并发问题?===没有,有类加载器保障
public static SingleDemo5 getInstance(){
return SingleStatic.INSTANCE;
}
//2.属于懒汉式还是饿汉式?===懒汉式,只有调用时才会初始化静态内部类
static class SingleStatic{
private static final SingleDemo5 INSTANCE=new SingleDemo5();
}
}
破坏与防止
反射
//SingleDemo1 防止反射破坏
private SingleDemo1(){
if (INSTANCE!=null){
throw new RuntimeException("请通过 getInstance()方法获取");
}
}
序列化
重写反序列化方法readResolve(), 反序列化时直接返回相关单例对象
public Object readResolve(){
return INSTANCE;
}