JAVA单例之我见

为什么80%的码农都做不了架构师?>>>   hot3.png

       单例模式作为设计模式中最简单的一种,是一个被说烂了的东西。但是在项目中还是会发现关于单例模式的一些错误实现,可见单例也并不是我们想象的那么简单。最近陆陆续续看了几篇关于单例的博客,很受启发,所以觉得有必要总结一下(只涉及常用的双重检查锁定、静态内部类、枚举三种单例实现方法),本文将从安全和性能两个方面阐述我对单例模式三种最佳实践的理解。不当之处,请 指正。

1、双重校验锁定

 

//Double Checked locking
public class SingletonByDCL
{private Map<Integer, String> configMap;private volatile static SingletonByDCL instance = null;private SingletonByDCL(Map<Integer, String> configMap){this.configMap = configMap;}public static SingletonByDCL getInstance(){SingletonByDCL inst = instance;if (null == inst){synchronized (SingletonByDCL.class){inst = instance;if (null == inst){inst = new SingletonByDCL(ConfigReader.configMap);instance = inst;}}}return inst;}
}

 

通过synchronized和volatile实现了线程安全。其中需要注意的是实例变量一定要用volatile修饰。原因可参考Java 单例真的写对了么?。当单例对象需要被序列化时,就应该考虑单例实现的序列化安全,在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以方式单例被破坏,原理可参考单例与序列化的那些事儿。可能会有人使用反射强行调用我们的私有构造器,为了保证访问安全,可以修改构造器,让它在创建第二个实例的时候抛异常。

2、静态内部类

 

public class SingletonByInnerStaticClass
{private Map<Integer, String> configMap;private SingletonByInnerStaticClass(Map<Integer, String> configMap){this.configMap = configMap;}private static class SingletonHolder{private static SingletonByInnerStaticClass instance = new SingletonByInnerStaticClass(ConfigReader.configMap);}public static SingletonByInnerStaticClass getInstance(){return SingletonHolder.instance;}
}

 

线程安全,这是 Java 运行环境自动给保证的,在加载的时候,会自动隐形的同步。在访问对象的时候,不需要同步 Java 虚拟机又会自动取消同步。对于序列化安全和访问安全的保证,解决方法同“双重检查锁定”。

3、枚举

 

public enum SingletonByEnum
{instanse(ConfigReader.configMap);private Map<Integer,String> configMap;private SingletonByEnum(Map<Integer,String> configMap){this.configMap = configMap;}public Map<Integer, String> getConfigMap(){return configMap;}
}

当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型(可用javap查看enum编译后的class文件,从而了解enum包含哪些静态资源)是线程安全的。为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定,从而保证序列化安全。

 

private static void testSingletonByEnum() throws IOException, FileNotFoundException, ClassNotFoundException{// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempfile"));oos.writeObject(SingletonByEnum.instanse);// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("tempfile"));SingletonByEnum s = (SingletonByEnum) ois.readObject();// 判断是否为同一对象if (s == SingletonByEnum.instanse){System.out.println("创建的是同一个实例");} else{System.out.println("创建的不是同一个实例");}}

 

创建的是同一个实例

 

 

 

同时java.lang.reflect.Constructor的newInstance()方法中有如下代码,禁止了通过反射构造枚举对象,所以枚举可以保证访问安全。关于枚举如何保证线程安全和序列化安全,可参考深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题

if ((clazz.getModifiers() & Modifier.ENUM) != 0)   throw new IllegalArgumentException("Cannot reflectively create enum objects");

4、三种方法的性能比较

三种方法都实现了延迟加载,在8线程同时调用,每个线程调用100000000次的情况下时间对比如下:

SingletonByDCL—》845

SingletonByEnum—》90

SingletonByInnerStaticClass—》89

具体测试代码参见附件工程中的com.zjg.perf.PerformanceTest2

综上所述,Effective Java中推荐使用的枚举实现单例无论从安全还是性能都是有道理的。当然代码没有一劳永逸的写法,只有在特定条件下最合适的写法。在不同的平台、不同的开发环境(尤其是jdk版本)下,也就会有不同的最优解。比如枚举在Android平台上却是不被推荐的。在这篇Android Training中明确指出:

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

再比如双重检查锁法,不能在jdk1.5之前使用,而在Android平台上使用就比较放心了(一般Android都是jdk1.6以上了,不仅修正了volatile的语义问题,还加入了不少锁优化,使得多线程同步的开销降低不少)。

参考文章:

http://blog.jobbole.com/94074/  深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题

http://www.importnew.com/18872.html  你真的会写单例模式吗——Java实现

http://www.hollischuang.com/archives/1144  单例与序列化的那些事儿

http://www.race604.com/java-double-checked-singleton/?utm_source=tuicool&utm_medium=referral Java 单例真的写对了么?

demo工程:https://git.oschina.net/zjg23/SingletonDemo

转载于:https://my.oschina.net/zjg23/blog/671445

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「刘一哥GIS」系列专栏《QGIS入门实战精品教程(配套案例数据)》

「刘一哥GIS」系列专栏《QGIS入门实战精品教程&#xff08;配套案例数据&#xff09;》全新上线了&#xff0c;欢迎广大GISer朋友关注&#xff0c;一起探索GIS奥秘&#xff0c;分享GIS价值&#xff01; 本专栏以实战案例的形式&#xff0c;深入浅出地介绍了QGIS的基本使用方法&…

MyEclipse的Git配置

1.下载&#xff1a;git的插件egit 并解压 插件 下载地址&#xff1a;http://www.eclipse.org/egit/download/ 所有版本&#xff1a;http://wiki.eclipse.org/EGit/FAQ#Where_can_I_find_older_releases_of_EGit.3F 2.在MyEclipse安装目录下的dropins文件夹下创建egit文件夹 3.…

VSCode 插件开发实例(WebView):微信读书 ^-^边撸代码边看小说^-^

最终效果 主要代码 package.json {"name": "WeReadForVSCodeJackieZheng","repository": {"type": "git","url": "https://github.com/JackieZheng/WeReadForVSCode.git"},"displayName": &q…

【QGIS入门实战精品教程】4.7:QGIS如何将矢量数据转为GeoJSON格式?

本文以案例的形式,讲述在QGIS专业软件中,将矢量数据转为GeoJSON的方法。 扩展阅读: 【ArcGIS风暴】如何将矢量数据(点、线、面)折点坐标转为GeoJSON格式? 在QGIS中,可以直接将数据导出为GeoJSON格式。具体操作方法是:右键矢量数据图层→导出图层→到文件,如下图所示:…

Git的纯命令操作,Install,Clone , Commit,Push,Pull,版本回退,撤销更新,分支的创建/切换/更新/提交/合并,代码冲突...

Git的纯命令操作&#xff0c;Install&#xff0c;Clone , Commit&#xff0c;Push&#xff0c;Pull&#xff0c;版本回退&#xff0c;撤销更新&#xff0c;分支的创建/切换/更新/提交/合并&#xff0c;代码冲突 这篇是接着上篇分布式版本库——Windows下Git的环境部署以及在Git…

【QGIS入门实战精品教程】3.3:QGIS如何打开ArcGIS创建的文件数据库(GDB)?

在行业应用中,GIS地理空间数据往往存储在ESRI ArcGIS的文件地理数据库(File GeodataBase),因此,ArcGIS与QGIS的数据交互、共享就显得非常重要。QGIS3可以直接打开File GDB数据,并对数据进行显示、查看、处理等操作。具体的步骤如下: 相关阅读:【QGIS入门实战精品教程】3…

.NET 6 Linux 系统服务 Systemd (案例版)

.Net Core Windows 系统服务&#xff0c;之前已经写过了&#xff0c;但是&#xff0c;对于Linux的系统服务&#xff0c;却没有写过&#xff0c;主要是因为&#xff0c;大部分Linux的.Net服务都直接Docker了&#xff0c;当然&#xff0c;不排除有这种使用的&#xff0c;可能搞成…

[转]IaaS、PaaS、SaaS、CaaS、MaaS五者的区别

云计算构架图 很明显&#xff0c;这五者之间主要的区别在于第一个单词&#xff0c;而aaS都是as-a-service&#xff08;即服务&#xff09;的意思&#xff0c;这五个模式都是近年来兴起的&#xff0c;且这五者都是云计算的落地产品&#xff0c;所以我们先来了解一下云…

聊聊 C++ 中的四种类型转换符

一&#xff1a;背景 在玩 C 的时候&#xff0c;经常会用 void* 来指向一段内存地址开端&#xff0c;然后再将其强转成尺度更小的 char* 或 int* 来丈量一段内存&#xff0c;参考如下代码&#xff1a;int main() {void* ptr malloc(sizeof(int) * 10);int* int_ptr (int*)ptr;…

【QGIS入门实战精品教程】3.1:QGIS如何连接SQL Server数据库?

文章目录 软件必备测试SQL Server服务是否启动QGIS与SQL Server建立连接软件必备 SQL Server 2008 R2QGIS 3.22.3测试SQL Server服务是否启动 打开SQL Server配置管理器,查看MSSQLSERVER服务的启动情况。

开源重量级的流程引擎或UI引擎

关注这两个方面的同学个踊跃加粉了~~同时在下面回复期望开源哪一个&#xff0c;将先开源呼声高的一个。 好吧&#xff0c;先小秀一下UI&#xff1a; 增加下面的pom依赖&#xff0c;表示此工程需要org.tinygroup.aerowindow ?12345<dependency> <groupId>org.tinyg…

阿里云网盘内测开启 填写申请表获取邀请码 附最新申请地址

日前有相关报道显示&#xff0c;阿里方面推出了一款名为“阿里云网盘”的独立App&#xff0c;并且其是由阿里云团队开发&#xff0c;定位则是为C端用户提供可靠安全的存储备份及智能相册等功能。据了解&#xff0c;目前这款APP尚未正式上线&#xff0c;暂时还在邀请制测试阶段。…

跟我做⼀个⾼德地图的 iOS / Android MAUI控件(Android 原⽣库绑定)

我们已经介绍了如何通过 .NET 绑定 iOS 原⽣库 &#xff0c;本篇开始介绍⼀下如何通过 .NET 绑定 Android 原⽣库。Android的库Android 的库以 .jar 做打包&#xff0c; 通过⼯具你可以将多个 .jar 完成绑定&#xff0c;然后通过 C# 调⽤原⽣的 Java 库。对⽐起 iOS &#xff0…

【QGIS入门实战精品教程】4.6:QGIS实现栅格(影像、DEM)的拼接与掩膜提取

参考阅读:ArcGIS实验教程——实验十一:影像拼接与提取 加载实验数据 本实验所采用的栅格数据为两个dem数据和一个矢量范围数据,加载如下图所示: 栅格数据信息如下: 栅格拼接 点击下拉菜单【栅格】→【杂项】→【合并(merge)】,如下所示:

ReSharper 2020.2 补丁

ReSharper 是一个JetBrains公司出品的著名的代码生成工具。其能帮助Microsoft Visual Studio成为一个更佳的IDE&#xff0c;它包括一系列丰富的能大大增加C#和Visual Basic .net开发者生产力的特征。使用ReSharper&#xff0c;你可以进行深度代码分析&#xff0c;智能代码协助…

零中频接收机主要问题

直流偏差和本振泄漏问题基本不会影响超外差式接收机的性能&#xff0c;问题主要是镜频抑制。需要高Q值的带通滤波器。 零中频不存在镜频干扰&#xff0c;可以省掉镜像抑制滤波器和中频滤波器。零中频的主要问题是&#xff1a;1直流偏差 2本振泄漏 3 闪烁噪声。 1 本振泄漏 本振…

【QGIS入门实战精品教程】9.1:QGIS构建泰森多边形(Thiessen Polygon)实例精解

泰森多边形是进行快速插值和分析地理实体影响区域的常用工具。例如,用离散点的性质描述多边形区域的性质,用离散点的数据计算泰森多边形区域的数据。泰森多边形可用于定性分析、统计分析和临近分析等。 参考教程: ArcGIS构建泰森多边形(Thiessen Polygon)实例精解 【Glob…

“Visual Studio 启动不能打开上次打开的文件” 最正确的解决姿势

网上很多提供的方法&#xff0c;不是删除.vs目录&#xff0c;就是修改.suo文件。 删除有风险&#xff0c;操作需谨慎&#xff01;&#xff01; 其实最简单的方法就是&#xff1a;工具——选项——项目和解决方案——☑加载解决方案时重新打开文档(D)

【QGIS入门实战精品教程】5.1:QGIS地理坐标转火星坐标系(GCJ02)案例教程

本文以案例的形式,讲述WGS84(GPS)、火星坐标系(GCJ02)、百度地图(BD09)坐标系之间互相转换。 一、WGS转火星坐标系对比 文中将行政区的WGS坐标转为火星坐标系,局部效果对比: 二、火星坐标系简介 火星坐标系统是一种国家保密插件,也叫做加密插件或者加偏或者SM模组,其…

spark streaming 的 Job创建、调度、提交

2019独角兽企业重金招聘Python工程师标准>>> 上文已经从源码分析了Receiver接收的数据交由BlockManager管理&#xff0c;整个数据接收流都已经运转起来了&#xff0c;那么让我们回到分析JobScheduler的博客中。 // JobScheduler.scala line 62def start(): Unit sy…