文档章节

单例模式、双重锁、无序写入

lmqian
 lmqian
发布于 2017/05/13 14:41
字数 959
阅读 26
收藏 0
点赞 1
评论 0

之前在看Jfinal的源码的时候看到了这样一段代码

public static Prop use(String fileName, String encoding)
{
    Prop result = (Prop) map.get(fileName);
    if(result == null)  //#1
        synchronized (map)
        {
            result = (Prop) map.get(fileName);
            if(result == null)
            {
                result = new Prop(fileName, encoding); //#2
                map.put(fileName, result);
            }
        }
    return result;
}

其中,map是一个常量。

刚开始,我很疑惑,为什么要在同步代码块内外都要检查result是否为null。细想了一下才想明白:线程安全与性能之间的均衡。

1、假设线程A在同步代码块外检查到result为null,在进入同步代码块之前,线程B更新了map的内容,将对应键值对放进了map中。此时线程a进入同步代码块,因为会再获取并检查一次result是否为null,就减少了一次文件IO(result是从文件中获取的数据)。

2、假设同步代码块之前不去检查result是否为null,那么多个线程调用这个方法的时候,就都要排队,性能降低。

看上面的代码逻辑,似曾相识:跟单例模式的懒汉式类似,即在需要用到的时候再去加载对象,而不是在类加载的时候就生成对象。

一般使用单例模式就是三步:创建私有对象;私有化构造器;声明获取对象的公共方法。

下面是单例模式延迟加载(懒汉式)的代码示例:

public class Singleton
{
    private static Singleton singleton;
    
    private Singleton()
    {}
    
    public static Singleton getInstance()
    {
        if(singleton == null)
        {
            singleton = new Singleton();
        }
        return singleton;
    }
}

结合前面讲到的,很明显在多线程情况下,不能保证每个线程使用的是同一个对象。那么,就可以用到上面的那种方式了。这种方式被称为“双重检查锁”机制。代码就略掉了。

一般情况下,我们理解声明一个变量并进行初始化的顺序是:栈中创建变量;分配堆内存;创建对象;将栈中变量指向对象的地址

实际上,java平台内存模型(JMM)允许“无序写入”,也就是说处理器可能按这样的顺序执行:栈中创建变量;分配堆内存;将栈中变量指向对象的地址;创建对象

由此而产生的问题就是:在上面第一段代码中,假设线程A执行到#2处,线程B执行到#1处理,然后#2出现了无序写入,那么有可能线程B判断为false,result不为空,但是还并没有真正初始化数据,线程B拿到的可能是一个数据不完成的对象。

网上看到有说可以用volatile关键字来解决这个问题,我表示不太认同。

volatile变量对所有线程都是立即可见的,对volatile变量的写操作其他线程能够立即可见。但是它并没有加同步锁,在上面的例子中,即便使用volatile修饰,线程B在某些时刻还是有可能取到不完整的对象。

那么,究竟怎么来解决这个问题呢?

  1. 检查前直接使用同步锁,就是前面提到的性能降低的方法。
  2. 不使用延迟加载方式,在类加载的时候就初始化单例对象。
  3. 静态内部类
public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

a. 满足了单例的要求。 b. 加载外部类的时候并不会加载内部类,只有第一次引用的时候才会加载内部类,使得单例对象是延迟创建的。

  1. Enum枚举
public enum Singleton{
    INSTANCE;
}

枚举的创建默认是线程安全的,缺点是没法继承和实现其他类。

© 著作权归作者所有

共有 人打赏支持
lmqian
粉丝 1
博文 4
码字总数 2681
作品 0
成都
程序员
1、单例模式

定义: 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。 特点: 1、单例类只...

晚天吹凉风
02/22
0
0
创建型模式.单例模式-懒汉、饿汉、枚举、原子

1 安全发布对象的四种方法 在多线程中,为了保证线程安全性,我们要正确地发布对象,保证发布地对象不要逸出。 在静态初始化函数中初始化一个对象引用 将对象的引用保存到volatile类型域或者...

阿杜杜不是阿木木
03/28
0
0
设计模式(创建型)之单例模式(Singleton Pattern)

转载来自:http://blog.csdn.net/yanbober/article/details/45312675 PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊! 【...

xiaopangzi520
06/26
0
0
[设计模式]单例模式

简介 单例模式(Singleton Pattern)保证一个类只有一个实例,并提供一个访问它的全局访问点。 单例模式是一种对象创建型模式 (可参考 设计模式 创建型模式)。 单例模式是设计模式中最简单的...

文艺小青年
2017/11/16
0
0
Java设计模式之单例设计模式

Java单例设计模式,有多种实现方式,下面介绍一下比较著名的一些实现方式 饿汉式 这种设计模式简单,且没有多线程安全问题,一般实际开发时选用这种方式。 class HungeryDemo{private Hunger...

技术小胖子
2017/11/08
0
0
炒冷饭系列:设计模式 单例模式

炒冷饭系列:设计模式 单例模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 亲爱我,孝何难;亲恶我,孝方贤。 一、什么是单例模式 单例模...

泥沙砖瓦浆木匠
2014/07/28
0
0
为什么我墙裂建议大家使用枚举来实现单例。

关于单例模式,我的博客中有很多文章介绍过。作为23种设计模式中最为常用的设计模式,单例模式并没有想象的那么简单。因为在设计单例的时候要考虑很多问题,比如线程安全问题、序列化对单例的...

06/10
0
0
我的Java设计模式-单例模式

  就算不懂设计模式的兄弟姐妹们,想必也听说过单例模式,并且在项目中也会用上。但是,真正理解和熟悉单例模式的人有几个呢?接下来我们一起来学习设计模式中最简单的模式之一——单例模式...

Jet啟思
2017/10/26
0
0
javascript 设计模式之工厂(Factory)模式

工厂模式介绍 工厂模式是一个创建型的模式,主要就是创建对象。其中工厂模式又分为简单工厂模式和抽象工厂模式。简单工厂模式是通过工厂方法确定创建 对应类型的对象。抽象工厂模式是通过子类...

hlxiong
2014/04/14
0
0
java设计模式-- 单例模式

在很久之前,也就是在大二暑假的时候,那时候看马士兵的视频教程中有提到很多的设计模式。 java的设计模式大致可以分为3大类,23种设计模式。 其中,创建型模式有5种:单例模式、建造者模式、...

爱学习的逃课君
2014/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Android 获取各大音乐平台的真实下载地址

废话 电脑使用谷歌浏览器或者QQ浏览器的时候。。。。。。。说不清楚,还是看图吧 大概意思就是,只要网页上需要播放,只要能播放并且开始播放,这个过程就肯定会请求到相关的音乐资源,然后就...

她叫我小渝
33分钟前
0
0
shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
今天
2
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
今天
1
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
2
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
1
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部