单例模式面试考点

张开发
2026/4/18 10:15:35 15 分钟阅读

分享文章

单例模式面试考点
1什么是单例模式*一个类全局只能有一个变量不能new多个对象。*构造其私有提供全局访问入口。2单例模式的核心是什么*构造器私有private*自身持有私有静态实例*提供静态公共方法供外部拿到实例3单例模式的分类*单例模式有多种类型常见的有饿汉模式懒汉模式两种饿汉模式线程安全优点线程安全无锁性能高快缺点类加载就初始化因此有可能会出现在有多个单例模式的情况下出现扎堆初始化的情况从而造成卡顿还有可能浪费内存。懒汉模式线程不安全优点可以有程序员自己决定实例的创建时机即在第一次调用getInstance方法时。缺点存在线程安全问题但可以一些手段来解决。懒汉模式的线程安全问题1多个线程同时判空导致创建出多个实例违背了单例模式的设计由于Java线程是抢占式调度线程的调度是随机的所以就会出现下面这种情况线程1刚判断 instance 是否为空就被调度出cpu并且保留上下文接着线程2开始执行线程2接着判断实例为空依然满足接着往下执行创建了一个实例线程2执行完线程1又被调度到CPU上恢复执行恢复上下文紧接着又创建了一个实例。但是最终只会留下一个实例线程2创建的实例最终没有内存指向就会被GC回收。但是即使这样代码依然是存在bug的比如线程2已经把返回的实例拿去使用了而且也违背了单例模式的设计契约。解决这个问题就需要把条件判断和创建实例整体加锁变成原子操作。2双重DCl检查锁Double - Checked Locking 在初始化实例之前多线程调用当前getInstance方法此处的锁当然是有意义的但是一旦初始化完成此处再把锁加上后续再调用getInstance就可能产生锁竞争导致阻塞但实际上这样的阻塞其实是没有什么意义的一旦阻塞代码就和高性能无缘了因为此时唯一的实例已经创建好了已经线程安全了。所以我们就需要在进行一次条件判断以便在实例创建好之后其他线程再调用方法时就不用再次加锁了。总结一下为什么要两次判空第一次判空是为了避免重复加锁提高性能。第二次判空是因为防止在多线程的情况下多个线程同时进入第一次if重复创建实例。3指令重排序问题代码进行 new 对象的过程分为三步1分配内存空间JVM会先把变量设为默认值2对象初始化3引用赋值引用赋值就代表引用已经不为空了但是在重排序的情况下可能会出现132的执行顺序进而在多线程的情况下其他线程再进行第一层条件判空时会直接返回实例此时其他线程就拿到了一个未初始化的实例进而有可能拿着引用调用其他方法就会出现bug所以为例禁止代码在执行的时候进行重排序就需要使用volatile关键字。

更多文章