DP #1 Singleton Pattern线程安全问题
单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例。
其中涉及到最主要的问题就是在多线程并发时线程安全问题。
单例模式的实现也有一个循序渐进的过程:
1.最基本要求:每次从getInstance()都能返回一个且唯一的一个Singleton对象。
2.稍微高一点的要求:能适应多线程并发访问。
3.再提高一点的要求:提高获得Singleton实例的性能。
4.最后一点要求是:实现懒加载(Lazy Load),在需要的时候才被构造。
——————————————————————————————–
1. 单例模式最基本的实现 = 私有构造函数 + 私有静态属性 + 公有静态Getter方法判断实例化
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
——————————————————————————————–
2. 如果考虑线程并发,最直接的想法就是加线程锁(synchronized)来解决线程同步问题,可以适应多线程并发,但是这里有个很大的性能问题,每一次调用getInstance都会进行同步准备,代价很大,其实我们只有在创建实例的问题上才需要解决线程安全问题。
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
——————————————————————————————–
3. 为了提高性能,我们只希望在第一次创建Instance实例的时候进行同步,使用双重检测加锁(DCL)机制,判断是第一次实例化后锁定对象,然后再synchronized块中再次判断。
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized(Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
DCL的写法来实现单例是很多资料上推荐的写法,实际上是不完全正确的。代码看似没有问题,但是测试中却发现线程大量并发访问时会报NullPointException,调试很久没有结果。
主要原因是因为 uniqueInstance = new Singleton();这句代码并非原子操作,在JVM执行的汇编代码中分为以下几步:
步骤1:给Singleton的实例分配内存空间。
步骤2:初始化Singleton构造器
步骤3:将uniqueInstance指向第一步中分配的内存空间。
步骤2和步骤3在JMM中是无法保证顺序的,所以如果1-3-2的情况发生,将造成RuntimeException:
线程A进入同步块,执行步骤1-3,此时uniqueInstance已经指向了内存空间,但是Singleton还未构造,此时uniqueInstance已经非空,线程B直接return uniqueInstance。
关于添加volatile关键字,jvm虚拟机保证每次uniqueInstance的值都是从主内存中读取,这种方案是可行方案之一。
——————————————————————————————–
4. 重新设计后,我们把初始化实例的事情扔给JVM。因为JVM保证一个类在一个ClassLoader中只会被初始化一次。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
这是传说中的【饿汉式单例】,全局的单例在类装载时第一时间被构建并初始化。
优点:速度快,提前创建。
缺点:某些需要配置参数传入getInstance构建的Singleton无法使用
——————————————————————————————–
5. 我们还是回归第一次被使用时创建的懒加载,同样把初始化实例的事情交给JVM,这次我们使用内部私有类来创建实例。
public class Singleton {
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
static final Singleton INSTANCE = new Singleton();
}
}
这是【懒汉式单例】的一种,SingletonHolder是私有内部类,所以只有getInstance可以访问,读取实例的时候也不会同步,性能上和安全上比较平衡。
——————————————————————————————–
结语:
单例模式的实现方式还有很多,在不同语言不同需求下的实现也有不同的考量和标准,作为一个最简单又最复杂的设计模式,我们应该去尝试每种实现的潜在问题和解决方案,需要更多的理解和尝试。
DP #1 Singleton Pattern线程安全问题的更多相关文章
- singleton pattern的推荐实现
一.单例模式的C#实现: (1)使用double-checked locking的方式: public sealed class Singleton { private static volatile ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- Java 设计模式(三)-单例模式(Singleton Pattern)
1 概念定义 1.1 定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2 类型 创建类模式 1.3 难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...
- Java设计模式—单例设计模式(Singleton Pattern)全然解析
转载请注明出处:http://blog.csdn.net/dmk877/article/details/50311791 相信大家都知道设计模式,听的最多的也应该是单例设计模式,这种模式也是在开发中用 ...
- 设计模式01 创建型模式 - 单例模式(Singleton Pattern)
参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...
- 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性
模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...
- Singleton Pattern -- 不一样的单例模式
Singleton Pattern -- 单例模式 单例模式是用来创建一个只能又一个实例的对象. 单例模式类图如下. 单例模式有两大好处: (1)对于频繁使用的对象,可以省略创建对象所话费的时间,这对 ...
- Net设计模式实例之单例模式( Singleton Pattern)
一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
随机推荐
- SpringMVC整合fastjson-1.1.41
以前用fastjson也只是硬编码,就好像这篇博文写的http://blog.csdn.net/jadyer/article/details/24395015 昨天心血来潮突然想和SpringMVC整 ...
- ANDROID内存优化(大汇总——中)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- Citrix Presentation server can not contact the license server
If you come across the above error, you may also come across one or more of the errors below within ...
- mvc和webapi同一解决方案调试办法
今天在研究WebApi的时候,用mvc端直接请求webapi接口,发现怎么也请求不了,自己搞了半天,猜测可能是webapi没有完全启动吧,解决办法是将解决方案属性改为多启动项目,具体方法如下: 直接运 ...
- 软键盘 输入法管理器 InputMethodManager
基本介绍 软键盘的显示原理 软键盘其实是一个Dialog.InputMethodService为我们的输入法创建了一个Dialog,并且对某些参数进行了设置,使之能够在底部或者全屏显示.当我们点击输 ...
- Html.RenderPartial和Html.RenderAction的区别
添加一个PartialController控制器 using System; using System.Collections.Generic; using System.Linq; using Sy ...
- 记一次T-SQL查询优化 索引的重要性
概述 在一次调优一个项目组件的性能问题时,发现SQL的设计真的是非常的重要,所以写一篇博文来记录总结一下. 环境介绍 这个项目组件是一个Window服务,内部在使用轮循机会在处理一个事件表中的事件,将 ...
- C#操作Office- Cannot find the interop type that matches the embedded interop type 'Microsoft.Office.Interop.Excel.Application'
网上说 2003 -> 11.0, 2007 -> 12.0. 因为平时提示"Are you missing an assembly reference?",都是没有引 ...
- 系统管理中 bash shell 脚本常用方法总结
在日常系统管理工作中,需要编写脚本来完成特定的功能,编写shell脚本是一个基本功了!在编写的过程中,掌握一些常用的技巧和语法就可以完成大部分功能了,也就是2/8原则 1. 单引号和双引号的区别 单引 ...
- 解决Cacti监控图像断断续续问题
最近cacti的图像全都是断断续续.新加的设备,图像也是这样,查看cacti 的log发现大量下面类似的错误信息:04/12/2011 03:54:37 PM - SPINE: Poller[0] H ...