单例模式

简介: 本文主要介绍内容:1. 单例及单例模式概念2. 实现单例模式注意点3. 单例模式的实现方式

单例及单例模式概念

一个类只允许创建一个实例。这种设计模式就是单例模式

         1617762041548.png


实现单例模式需要考虑什么?

  • 构造函数私有,避免外部通过new来创建实例
  • 考虑是否需要延时加载
  • 考虑线程安全


单例模式的几种实现方式

1. 饿汉式:在类加载的时候,就已经初始化实例了。

publicclassHungry {
// 一启动就被加载,还未使用就占用内存,造成资源浪费privatestaticfinalHungryINSTANCE=newHungry();
// 构造方法私有,外部无法进行实例化privateHungry() {}
// 获取实例对象publicstaticHungrygetInstance() {
returnINSTANCE;
  }
}


2. 懒汉式:第一次使用时才进行实例化

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {}
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
}

但是懒汉式并不能保证单例,在多线程并发情况下,会多次调用构造方法进行实例化,示例如下:

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {
System.out.println(Thread.currentThread().getName());
    }
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
publicstaticvoidmain(String[] args) {
for (inti=0; i<10; i++) {
newThread(Lasy::getInstance).start();
        }
    }
}

输出结果:

2.png


2.1 使用同步锁synchronized

//如果使用synchronized关键字,这种方式在并发下并发量等同于1,如果频繁使用该对象,会大大降低性能publicstaticsynchronizedLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
    }
returnINSTANCE;
}


2.2 双重检测(DCL懒汉式单例)

publicstaticLasygetInstance() {
if (null==INSTANCE) {
synchronized (Lasy.class) {
if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}
// INSTANCE = new Lasy();/** 以上操作并不是原子性操作,包括以下三个步骤:* 1. 分配内存空间* 2. 执行构造方法进行初始化* 3. 将对象指向这个空间*//** 在多线程情况下可能出现指令重排,每个线程执行INSTANCE = new Lasy()这个操作的顺序可能不一致,那么就有可能出现线程A先执行了第一步和第三步,此时还未进行初始化,线程B执行判断语句null == INSTANCE时为false,将直接返回线程A未进行初始化的INSTANCE对象,所以要避免指令重排,使用volatile关键字。*/privatestaticvolatileLasyINSTANCE;
publicstaticLasygetInstance() {
if (null==INSTANCE) {
// 对象未被实例化,等待类对象锁synchronized (Lasy.class) {
// 进入对象锁,其他线程等待,再次判断是否被实例化if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}

饿汉式和懒汉式比较:
①由于饿汉式是在类加载的时候进行实例化,线程安全;懒汉式是在第一次使用的时候进行实例化,此时可能多线程会同时进行实例化,所以线程不安全;
②饿汉式不支持延时加载,如果占用资源较多或者耗时较长,可能会影响启动时间;懒汉式支持延时加载,但是需要使用同步锁,如果这个类被频繁使用,可能导致性能问题。(相比较更加倾向于饿汉式单例,在启动时进行加载,如果遇到性能或者资源问题会提前暴露,相较于正在运行中出现问题更加可控)


3. 静态内部类

publicclassTest {
privateTest() {}
privatestaticclassInnerTest{
// final修饰变量是不可变的,初始化对象之后便不会再指向另一个对象privatestaticfinalTestINSTANCE=newTest();
    }
publicstaticTestgetInstance() {
returnInnerTest.INSTANCE;
    }
}

但是在反射机制下,所有的私有方法都能被破解,如下实例表示通过静态内部类获得的实例对象和通过反射获得的holder实例对象并不是同一个,仍然破坏了单例

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法Constructor<Test>constructor=Test.class.getDeclaredConstructor(null);
// 无视私有构造器constructor.setAccessible(true);
Testtest=constructor.newInstance();
System.out.println(Test.getInstance().equals(test));
}
输出结果:false

通过反射的源码可以看到,无法反射式创建枚举对象,所以我们可以考虑使用枚举来实现单例

1.png

4. 枚举

publicenumSingleEnum {
INSTANCE;
}


以下示例可见确实无法反射式创建枚举对象,当使用反射创建枚举对象时,会抛出异常信息Cannot reflectively create enum objects

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法(实际上枚举类的构造方法带有两个参数,并不是我们从源码看到的无参构造,需要使用源码工具来查看)Constructor<SingleEnum>constructor=SingleEnum.class.getDeclaredConstructor(String.class, int.class);
// 无视私有构造器constructor.setAccessible(true);
SingleEnumsingle=constructor.newInstance();
System.out.println(SingleEnum.INSTANCE.equals(single));
}

输出:

3.png

相关文章
|
安全 搜索推荐 前端开发
升级后的Sitecore DXP,为用户打造的未来值得期待吗?
收购电子营销工具Moosend、可组合商务平台Four51、客户数据平台Boxever、电商服务平台Reflektion,一切的迹象表明,Sitecore开始向着基于SaaS的可组合DXP数字体验平台过渡。
261 5
|
数据采集 数据可视化 数据挖掘
时间序列分析:用Python解锁金融市场数据的潜在价值
【4月更文挑战第12天】本文介绍了使用Python进行时间序列分析以挖掘金融市场数据价值的方法。金融市场数据具有时间性、不稳定性、非平稳性和相关性等特点。Python中的Pandas和Statsmodels库是进行时间序列分析的常用工具。基本流程包括数据导入、预处理、探索、模型选择(如ARIMA)、模型评估和优化。通过学习和实践,可以有效利用这些工具分析金融市场数据。
496 1
Vue3子传父 组件传参 defineEmits
Vue3子传父 组件传参 defineEmits
475 1
【进程通信】Syetem V 共享内存(结合代码模拟通信)
【进程通信】Syetem V 共享内存(结合代码模拟通信)
|
运维 Linux 网络安全
阿里云服务器ESC初体验
学生机使用感受
378 0
|
Android开发
[Android]activity详解
现在回头看看毕设做的安卓程序,不论从深度还是广度上对安卓开发都不够深入,这次从头复习,恰好遇到了一本让人欲罢不能的好书。 每一个安卓开发者都是从activity开始入门的,先整理一篇关于activity的知识点。
765 0
|
2天前
|
人工智能 弹性计算 运维
|
23天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)
|
9天前
|
人工智能 缓存 安全
Claude Code 封号真实原因曝光,这次彻底不装了,直接针对国内开发者的账号下手?
Claude Code 封号潮背后:逆向扒出客户端隐写区域标记,Anthropic 政策收紧叠加 DeepSeek 7 月涨价,国产替代更紧迫。