单例模式

简介: 本文主要介绍内容: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数字体验平台过渡。
272 5
|
数据采集 数据可视化 数据挖掘
时间序列分析:用Python解锁金融市场数据的潜在价值
【4月更文挑战第12天】本文介绍了使用Python进行时间序列分析以挖掘金融市场数据价值的方法。金融市场数据具有时间性、不稳定性、非平稳性和相关性等特点。Python中的Pandas和Statsmodels库是进行时间序列分析的常用工具。基本流程包括数据导入、预处理、探索、模型选择(如ARIMA)、模型评估和优化。通过学习和实践,可以有效利用这些工具分析金融市场数据。
513 1
Vue3子传父 组件传参 defineEmits
Vue3子传父 组件传参 defineEmits
492 1
【进程通信】Syetem V 共享内存(结合代码模拟通信)
【进程通信】Syetem V 共享内存(结合代码模拟通信)
|
运维 Linux 网络安全
阿里云服务器ESC初体验
学生机使用感受
397 0
|
Android开发
[Android]activity详解
现在回头看看毕设做的安卓程序,不论从深度还是广度上对安卓开发都不够深入,这次从头复习,恰好遇到了一本让人欲罢不能的好书。 每一个安卓开发者都是从activity开始入门的,先整理一篇关于activity的知识点。
776 0
|
8天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
1789 117
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
缓存 安全 IDE
1400 2
|
9天前
|
人工智能 程序员 API
Codex 接入 DeepSeek-V4-Flash:还能补上识图,提供两套方案
Codex 接入 DeepSeek-V4-Flash 怎么配?本文覆盖 CLI 与桌面端,再用 qwen3-vl-flash 补识图,两套方案可直接照做
1347 11
|
15天前
|
云安全 人工智能 运维
阿里云联动百位企业安全专家,共识Agent防御最佳实践
当Agent成为新员工,你的安全边界在哪里?
1962 9
阿里云联动百位企业安全专家,共识Agent防御最佳实践

热门文章

最新文章