单例模式

简介:

单例模式也叫单子模式,属于创建型模式。

单例模式可分为饱汉(懒汉)式单例和饥汉(饿汉)式单例两种。

单例设计模式是一种非常常见的设计模式,如果整个系统中允许某个类只存在唯一实例,那么就可以使用单例模式。

特别是:

1.初始化这个实例需要加载比较重的资源;

2.该实例要被频繁调用;

3.系统允许只有一个唯一实例;

那么,原则上必须使用单例模式。

单例模式看上去非常简单,但是在饱汉(懒汉)式单例模式的使用中,需要留意线程不同步和不同写法的代码在编译过程产生的单例bug,饥汉(饿汉)式单例则不存在这个问题。

这里推荐一种最规范的Java饱汉式单例模式写法,完全摆脱多线程和代码编译产生的单例

该写法由Google工程师Bob Lee发明,属于懒汉单例模式。解决了饱汉单例模式两大难题。

public class SingletonClass{
private SingletonClass(){} ;
private static class SingletonClassInstance{
private static final SingletonClass instance = new SingletonClass() ;
}
public static SingletonClass getInstance(){
return SingletonClassInstance.instance ;
}
}

1.饿汉式单例模式:

public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = new SingletonClass() ;
public static SingletonClass getInstance(){
return instance ;
}
}

看代码,这个实例是静态的,且在系统初始化的时候就已经初始化,并且永久存在。这就是饿汉式单例。因为是系统初始化的时候就存在,所以该实例不会因为线程的调度产生,所以不会存在线程安全问题。并且代码结构简约严谨,不会因为编译过程中的代码调优而改变代码的执行顺序,所以也不会产生编译bug。

下面看饱汉式单例的举例,我们通常所说的惰性加载(lazy load),程序初始化的时候单例实例并不初始化,需要等线程调用的时候才初始化这个实例。

2.饱汉式单例模式:

public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
if(instance == null){
instance = new SingletonClass() ;
}
return instance ;
}
}

在多线程环境中,CPU发生时间片切换,两个或多个线程有可能几乎是同一时间执行的,那么,这个实例同时被认为是null,就会多次被创建,虽然可能对性能的影响微乎其微,但是已经违反了单子模式的设计初衷。

如何既要使用懒汉式单例模式,又要保证线程安全?很多蹩脚的程序员自然想到了使用线程阻塞(synchronized关键字)。这很可笑,synchronized本来就对性能有很大影响,还有必要使用懒汉式单例吗?

下面我们给懒汉式单例加个锁:

public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public synchronized static SingletonClass getInstance(){
if(instance == null){
instance = new SingletonClass() ;
}
return instance ;
}
}

这样性能不好,换个好点的加锁方式:

public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
synchronized(SingletonClass.class){
if(instance == null){
instance = new SingletonClass() ;
}
}
return instance ;
}
}

看上去性能好像好多了,但是还是不够好:

public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
if(instance == null){
synchronized(SingletonClass.class){
if(instance == null){
instance = new SingletonClass() ;
}
}
}
return instance ;
}
}

这样看上去完美了。这就是双锁实现单子模式。

枚举

public enum Singleton2 {
INSTANCE ;
public void whatevermethod(){

}
}

 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

目录
相关文章
|
11月前
|
机器学习/深度学习 人工智能 弹性计算
阿里云AI服务器价格表_GPU服务器租赁费用_AI人工智能高性能计算推理
阿里云AI服务器提供多种配置,包括CPU+GPU、CPU+FPGA等组合,支持高性能计算需求。本文整理了阿里云GPU服务器的价格信息,涵盖NVIDIA A10、V100、T4、P4、P100等型号,适合人工智能、机器学习和深度学习等计算密集型任务。具体价格和适用场景详见表格。
535 10
|
运维 机器人 开发者
使用阿里云百炼通过appflow模板,组合钉钉机器人搭建个人知识库评测与感想
尝试构建个人助手机制,用阿里云百炼+AppFlow+钉钉机器人,花费两午休时间解决配置问题。百炼appid复制时多出空格致错,文档未提及,耗时排查。应用创建时模型选项限于max, plus, turbo,性价比高的qwen-long未上线。期望尽快修复bug和上线新模型以降低成本。附故障排查截图。
697 1
|
SQL 开发框架 .NET
初识天猫精灵语音技能
初识天猫精灵语音技能
680 0
初识天猫精灵语音技能
|
分布式计算 MaxCompute BI
【转载】时隔一年多,我又用起了 Superset
去年 6 月份在流利说提离职后,leader 问我为什么要走。我说,流利说有很健全的数据处理基础设施,但这不是所有的公司都会有的条件,所以我想看看在一个基建不全的创业公司我是否也可以像现在一样做的好。
17970 82
|
Windows
Win系统 - 文件夹或文件已在另一程序中打开怎么办?
Win系统 - 文件夹或文件已在另一程序中打开怎么办?
892 0
Win系统 - 文件夹或文件已在另一程序中打开怎么办?
|
机器学习/深度学习 人工智能 算法
机器之心独家解读:华为首款手机端AI芯片麒麟970
人工智能的最近一次浪潮起源于 2011 年前后深度学习(Deep Learning)引起的大发展。在其背后,快速发展的 GPU 功不可没。近年来,人们逐渐认识到计算芯片对于人工智能的重要性,围绕 AI 任务进行专有加速的芯片越来越多,但无论是 AlphaGo 背后的谷歌 TPU 还是加入了全新 Tensor Core 结构的英伟达 Tesla V100,这些芯片都是为服务器端进行设计的,在移动端对于机器学习任务加速的 SoC 还未出现。9 月 2 日,在德国柏林举行的 IFA 2017 展会上,华为正式发布了全球首款移动端 AI 芯片麒麟 970,一举填补了这一空白。
889 0
机器之心独家解读:华为首款手机端AI芯片麒麟970
|
数据采集 运维 Devops
企业构建统一CMDB数据源
本实践将指导企业客户在多账号的云上IT架构下,一站式的采集全量资源配置数据、资源配置历史、资源关系数据,将这些数据稳妥留存并快捷消费到自有的CMDB平台,加快企业自有CMDB的构建。
企业构建统一CMDB数据源
|
存储 云计算
云计算知识第三讲:云计算的产生和特点
传统IT技术存在利用率低,成本高,维护效率低,能源消耗高等诸多问题,在这种背景下,云计算在2007年被提出。
云计算知识第三讲:云计算的产生和特点
|
机器学习/深度学习 SQL 存储
基于Flink的机器学习平台Alink
PAI平台参加“周二开源日”活动,本期分享核心内容摘要: 一、Alink是什么; 二、Alink如何使用; 三、Alink进阶。
2239 0
基于Flink的机器学习平台Alink