单例模式
单例模式,顾名思义就是在整个运行时域,一个类只有一个实例对象
为什么要用单例呢。因为有的类的,比较庞大和复杂,如果频繁创建和销毁对象,而且这些对象是可复用的话,就会造成不必要的性能浪费。
单例模式的写法?
多种写法,考虑三点:
-
是否线程安全
-
是否懒加载
-
能否反射破坏
1 public class Singleton { 2 //懒加载,线程不安全 3 private static Singleton instance = null; 4 5 private Singleton() { 6 } 7 8 public static Singleton getInstance() { 9 if (instance == null) { 10 instance = new Singleton(); 11 } 12 return instance; 13 } 14 }
上面的单例是线程不安全的,因为在执行if (instance == null)
时,可能会有多个线程同时进入,从而实例化多次
那么如果把getInstance()
的签名改成:public static synchronized Singleton getInstance() {}
可以吗?
这样确实能线程安全,但是我们只需要对象在构建的时候同步线程,上面时每次获取实例的时候都同步线程,开销极大 。
线程安全问题出现在了构建对象阶段,那么我们只要在编译器构建对象,在运行时调用,就不用考虑线程安全问题了,于是我们这么写:
public class Singleton { //懒加载,线程不安全 private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
我们是想在构建对象的时候同步,而可以直接使用对象的时候就没必要同步,所以可以这么写:
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if instance = new Singleton(); } } return instance; } }
但是这么写会有问题:可能有多个线程进入 if (instance == null) {}
代码块 ,虽然只有一个线程A能拿到锁,但是它一释放,线程B就会立即获取锁进行对象创建,对象就被创建了多次。
可以使用双检锁:
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { //双检锁 if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
但是上面的写法也会遇到问题:
instance = new Singleton()
在指令层面并不是一个原子操作,而是分为三步:
-
分配内存
-
初始化对象
-
对象指向内存地址
在真正执行时,JVM为了执行效率,可能会对指令重排,比如 1 3 2
如果按照这个顺序,A线程执行到了第三步,还未初始化。假设就在此时,线程B执行到了 if (instance == null)
,false直接跳过,
返回instance导致对象未初始化。
解决办法:给instance加上volatile修饰
public class Singleton { //懒加载,线程安全 //volatile修饰的变量不能被指令重排 private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { //双检锁 if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
那有没有简单一点的写法呢:静态内部类
public class Singleton { //懒加载,线程安全 private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
懒加载:静态内部类在程序启动的时候不会加载,只有第一次被调用的时候才会加载。
线程安全:多线程时,类加载机制能实现线程安全,因为loadClass方法使用了synchronized
上述写法都可以被反射破坏,因为反射可以获取到类的构造函数,包括私有构造函数,因此反射可以生成新的对象
单例模式之枚举实现 https://blog.csdn.net/lovelion/article/details/110983839
public enum Singleton { INSTANCE; }
在实现过程中,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构