单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例。

其中涉及到最主要的问题就是在多线程并发时线程安全问题。

单例模式的实现也有一个循序渐进的过程:
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线程安全问题的更多相关文章

  1. singleton pattern的推荐实现

    一.单例模式的C#实现: (1)使用double-checked locking的方式: public sealed class Singleton { private static volatile ...

  2. 【设计模式】单例模式 Singleton Pattern

    通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance)  的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...

  3. Java 设计模式(三)-单例模式(Singleton Pattern)

    1     概念定义 1.1   定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2   类型 创建类模式 1.3   难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...

  4. Java设计模式—单例设计模式(Singleton Pattern)全然解析

    转载请注明出处:http://blog.csdn.net/dmk877/article/details/50311791 相信大家都知道设计模式,听的最多的也应该是单例设计模式,这种模式也是在开发中用 ...

  5. 设计模式01 创建型模式 - 单例模式(Singleton Pattern)

    参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton  Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...

  6. 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性

    模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...

  7. Singleton Pattern -- 不一样的单例模式

    Singleton Pattern -- 单例模式 单例模式是用来创建一个只能又一个实例的对象. 单例模式类图如下. 单例模式有两大好处: (1)对于频繁使用的对象,可以省略创建对象所话费的时间,这对 ...

  8. Net设计模式实例之单例模式( Singleton Pattern)

    一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...

  9. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

随机推荐

  1. linux定时任务1-crontab命令

    简单测试例子: 添加定时任务前,注意查看crond服务是否已经启动,如果未启动,则用命令service crond start命令启动. 注意给脚本添加可执行权限. [root@rheltest1 ~ ...

  2. linux下U盘的读取

    1.虚拟机vmware右下角,找到大容量存储设备图标,右键->connect(disconect from host):使U盘连接到虚拟机中来. 2.打开终端:fdisk -l [root@lo ...

  3. karma、jasmine做angularjs单元测试

    引用文:karma.jasmine做angularjs单元测试 karma和jasmine介绍 <1>技术介绍 karma karma是Testacular的新名字 karma是用来自动化 ...

  4. webform初识

    webform是个bs结构的程序, winform 是个cs结构的程序: aspx 是由 网页和cs代码 构成的: aspx的网页控件是 有, 服务器控件和客户端控件组成的. 客户端控件,就是HTML ...

  5. await, anync

    public Form1() { InitializeComponent(); } // The following method runs asynchronously. The UI thread ...

  6. php按址传递bug

    foreach ($product_info as $key=>&$value){            $value['bookCity'][]=array(              ...

  7. Phonegap 版本minSdkVersion为8的时候的自动更新与升级

    清单文件中: <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8"/> ...

  8. Asp.Net WebApi 启用CORS跨域访问指定多个域名

    1.后台action指定 EnableCors指定可访问的域名多个,使用逗号隔开 //支持客户端凭据提交,指定多个域名,使用逗号隔开 [EnableCors("http://localhos ...

  9. Android学习手记(2) Activity生命周期

    1. 单个Activity的生命周期 当只有一个Activity的时候, 首先执行onCreate->onStart->onResume. 这时, 窗口便显示在屏幕上了. 然后我们按返回键 ...

  10. javascript判断浏览器

    function getExplorer() { //IE if (navigator.userAgent.indexOf("MSIE")>=0) { } //Firefox ...