java 双重检查加锁弊端

http://blog.csdn.net/axman/article/details/1089196

Java是在语言级提供对线程的支持,所以Java的内存模型分为主存储器和工作存储器.

[Main memory]主存储器就是实例所在的存储区域,所有实例本身都被放在主存储器中,当然这
句话本身就说明了实例的字段也在主存储器中,主存储器被实例的所有线程所共有.

[working memory] 工作存储器当然就是每个线程所专有的工作区域,当然其中有它们共有的
主存储器中的一些必要的如实例字段等数据的COPY

当然你千万要知道Java是运行在虚拟系统上的,我们说的主存储器和工作存储器都是在物理内存
中虚拟出来的.是在JVM内部而言的.

在JLS中,对字段的存取被规定为read/write,use/assign,lock/unlock 六个最小的操作(action)

对于取而言,当一个线程一次访问实例字段时,首先从主存储器复制该字段的值到工作存储器,
然后线程引用该值.

但是如果同一线程多次访问该字段,是否每次都是从主存储器复制到工作存储器再引用,这由具体
的jvm环境决定.

简单说有可能不从主存储器中复制而直接引用工作区的COPY

同样对于存,如果一个线程一次赋值实例字段,那么会在工作存储器中进行,然后由jvm决定什么时
候映射到主存储器.
而同一个线程如果多次反复对实例字段赋值,那么有可能只对工作存储器的COPY进行,只把最后的
结果同步到主存储器,当然也有可能是每次都把工作存储器和主存储器同步的.这也由具体的JVM决
定.

所以如果多个线程反复对同一实例字段存取,就有可能一个线程对这个字段的改变没有及时反映给
其它线程,因为至少必须被从工作存储器同步到主存储器才能其它线程知道,而在线程专有的工作
存储器中的值其它线程是不可访问的.

所以boolean isInterrupted = false;这一句有可能一个线程中断后在这个线程工作的范围内已经
设为true,但还没有立即被映射到主存储器时,其它线程还不知道.

同样,对于double check,我们来看它的问题:
在java与模式一书中,作者这对个问题的说明根本没有正确地理解.而且他一再说明是错误的例子
其实是正确的.

public MyObject{
    private static MyObect obj;
    public static getInstance(){
        if(obj == null){
            synchronized(MyObj.class){
                if(obj == null)
                    obj = new MyObject();
            }
        }
        return obj;
    }
}

这个例子根本不会存在问题.因为线程的同步保证了不会在同步块外部发生多线程调,而在同步块中
只有一个线程能执行,其赋值的结果在离开同步块时会强制映射到主工作区,也就是对obj的赋值一定
会在主工作区反映出来.所以作者举这个例子说明他根本没有理解为什么double check是不安全的.

我们来看下面的例子:
public MyObject{
    private static MyObect obj;
    private Date d = new Data();
    public Data getD(){return this.d;}
    public static MyObect  getInstance(){
        if(obj == null){
            synchronized(MyObect .class){
                if(obj == null)
                    obj = new MyObject();//这里
            }
        }
        return obj;
    }
}
一个线程A运行到"这里"时,对于A的工作区中,肯定已经产生一个MyObect对象,而且这时这个对象已经
完成了Data d.现在线程A调用时间到,执行权被切换到另一个线程B来执行,会有什么问题呢?

如果obj不为null,线程B获得了一个obj,但可能obj.getD()却还没有初始化.

为什么?既然obj已经可见了(线程A还没有离开同步块),而d却不可见呢?
如果d不可见,那么obj也应该为null啊?线程B应该等待A释放同步块啊?

事实上,对于"这里"这条语句,线程A还没有离开同步块.
因为没有"离开同步块"这个条件,线程a的工作区没有强制与主存储器同步,这时工作区中有两个字段
obj,d 到底先把谁同步到主存储区,没有条件限制,虽然在线程A的工作区obj和d都是完整的,但有JSL
没有强制不允许先把obj映射到主存储区,如果哪个jvm实现按它的优化方案先把工作存储器中的obj
同步到主存储器了,这时正好线程B获取了,而d却没有同步过去,那么线程B就获取了obj的引用却找不能
obj.getD();

这个问题是否真的会发生?

我个人的意见,从理论上是会发生的,所以如果是设计银行交易系统你就没有必要为提高一些性能而放
弃安全性,而如果只是一般的应用,发生这种情况的机率本来就非常发生也不会有太多的危害,我认为
可以不去介意.比如获取数据库连结,即使获取不到也不过让访问者再刷新一次重新查询而已.事实上有
可能几天,几个月,一年都不会发生一次.知道小行星有可能会撞地球的,但你不要太在意而影响你正常
的生活.如果你不是做银行交易系统的你不要太担心,而大多数JVM实现本身就会保证这种安全的.也就 
在把obj同步到主存储器会先同步d,但万一发生相反的顺序,你就无权责备你所用的JVM.只是这种万一
机会不是很多.


当然如果是贪婪式调用,静态实例的初始化由ClassLoader经由[Thread Safe]来完成,当然就不会有这
个问题了:
public MyObject{
    private static MyObect obj = new MyObject();
    private Date d = new Data();
    public Data getD(){return this.d;}
    public static getInstance(){
        return obj;
    }
}


那么如何保证实例字段能在工作存储区能被即时映,下一节我们来讲最不常用的关键字

 

对于涉及对象初始化的DCL,从JAVA1.5以后虽然可以用volatile来修补,但已经没有任何意义。因为

 

比它更好的模式lazy initialization hoder可以达到相同的作用而更容易理解:

 

 

 

public MyObject{

 

 

 

    private static class instanceHoder{//内部私有的类,我特别用了小写开头。

 

        static MyObject instance = new MyObject();

 

    }
    private static MyObect obj;
    private Date d = new Data();
    public Data getD(){return this.d;}
    public static MyObect  getInstance(){

 

           return instanceHoder.instance;
    }
}

 

 

 

private static class instanceHoder是类的定义并不会引起初始化。只有在首次调用getInstance时才会加载

 

instanceHoder类然后初始化instance实例。而静态初始化是由JVM来保证线程安全的,所以整个过程都不需要同

 

步参与,极大地提高了性能。

转载于:https://my.oschina.net/vshcxl/blog/876852

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

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

相关文章

爬虫的复习手册

爬虫的概念 模拟浏览器发送请求,获取响应 爬虫的流程 url---》发送请求,获取响应---》提取数据---》保存 发送请求,获取响应---》提取url(下一页,详情页)重新请求 爬虫要根据当前url地址对应的响应为准 …

Hive安装报错:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient的解决办法

最近练习Hive,安装时爆出如下错误:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient的错误 报错的日志如下: Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeE…

要读

http://www.cnblogs.com/yangml/p/3828878.html转载于:https://www.cnblogs.com/qinqiu/p/6134683.html

Spark分布式集群的搭建和运行

集群共三台CentOS虚拟机,一个Matser,主机名为master;三个Worker,主机名分别为master、slave03、slave04。前提是Hadoop和Zookeeper已经安装并且开始运行。 1. 在master上下载Scala-2.11.0.tgz,复制到/opt/下面&#xf…

Hive2.1.1的安装教程(元数据放在本地Mysql)

目录1.上传tar包2.解压3. 设置环境变量4.设置Hive的配置文件5.启动Hive6.安装MySQL7.下载MySQL的驱动包8.修改Hive的配置文件9.启动Hive10.查看MySQL数据库 目录 1.上传tar包 jar包地址:http://hive.apache.org/downloads.html 2.解压 tar -zxvf apache-hive-2…

App性能优化之内存优化

2019独角兽企业重金招聘Python工程师标准>>> 为什么要进行内存优化呢?其实我们可以反过来想。如果不进行内存优化会产生什么样的问题? App的运行是有内存限制的,超过限制会产生OOM,导致App崩溃。如果内存不进行优化&am…

python+Tesseract-OCR实现图片识别(只适合新手)

1.首先准备环境: python版本:2.7/3.6 操作系统:windows系统 2.准备工具: tesseract-ocr 安装后设置好环境变量 链接: https://pan.baidu.com/s/1j8lBbQBrrbPaHAn5ujWFSw 提取码: 2med Pycharm 3.安装相关python包&#xf…

Linux 网络编程详解四(流协议与粘包)

TCP/IP协议是一种流协议,流协议是字节流,只有开始和结束,包与包之间没有边界,所以容易产生粘包,但是不会丢包。 UDP/IP协议是数据报,有边界,不存在粘包,但是可能丢包。 产生粘包问题…

解决selenium.common.exceptions.WebDriverException: Message: unknown error: call function result missin

(Session info: chrome73.0.3683.103)(Driver info: chromedriver2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platformWindows NT 10.0.17134 x86_64)报错如上,由于版本不兼容 下面是谷歌浏览器与chromedriver的版本对应关系,供参考&#…

执行Hive语句报错:FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user '

安装个Hive真不省心,各种问题。最近安装好Hive后执行Hive语句时碰到这样的错误: hive> show databases; FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user rootlocalhost (using password: YES) NestedThr…

GPU

import tensorflow as tf a tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[2,3],namea) b tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[3,2],nameb) c tf.matmul(a,b)sess tf.Session(configtf.ConfigProto(log_device_placementTrue)) print sess.run(c)

阿里云部署django项目流程【centos7+python3+mysql】

购买阿里云服务器 到[阿里云官网],选择轻量应用服务器, 步骤如图所示: 地域随便选择哪一个,镜像的话,对比了CentOS,Debian,Ubuntu,我最终选择了CentOS,因为流行嘛&…

XidianOJ 1123 K=1 Problem of Orz Pandas

题目描述 One panda named orz is playing a interesting game, he gets a big integer Num and an integer K. In this game, he can exchange two single numbers in Num. For example, he can get 1243 from 3241 by exchange 1 and 3.But orz can exchange at most K times…

对于频繁的写数据处理方式

添加一个新的表情的时候 调用 recentEmotions方法 将所有表情写入数组 每次都是 添加一个新的表情进来 要将沙盒中的所有表情首先加载进数组,然后将表情添加到数组里面 然后在将数组写入沙盒 处理方式 没有必要每次都要到沙盒里面读取数组文件 类方法 不能访问 成员…

在Mysql中显示所有用户的操作教程(Linux环境下)

1.登录数据库 首先,你需要使用如下命令登录到数据库,注意,必须是root用户哦~ mysql -u root -p 2.查询用户表 在Mysql中其实有一个内置且名为mysql的数据库,这个数据库中存储的是Mysql的一些数据,比如用户、权限信…

Scrapy 框架【学习笔记01】

Scrapy 框架 Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。 Scra…

通过profile 用maven命令打不同配置的变量包

profiles定义如下<profiles><profile><id>local</id><properties><deploy.type>local</deploy.type></properties></profile><profile><id>dev</id><properties><deploy.type>dev</de…

执行Hive的查询语句报错:java.lang.IllegalArgumentException: Does not contain a valid host:port authority: loca

好不容易把Hive装完了&#xff0c;结果一执行Hive的查询语句运行MapReduce程序立马报错。。。 log详细信息如下&#xff1a; Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 1; number of reducers: 0 2017-10-21 21:54:15,503…

scrapy startproject【学习笔记02】

入门案例 学习目标 创建一个Scrapy项目定义提取的结构化数据(Item)编写爬取网站的 Spider 并提取出结构化数据(Item)编写 Item Pipelines 来存储提取到的Item(即结构化数据) 一. 新建项目(scrapy startproject) 在开始爬取之前&#xff0c;必须创建一个新的Scrapy项目。进入…

开始把其他的博客搬家到这里了

今天&#xff0c;用一晚上的时间进行一下文章的迁移吧。这样以后查询就可以在自己的博客中查找了&#xff0c;也算是给自己一个写作的规律。 从很多个大牛的博客中都阅读到&#xff0c;写博客对于一个coder的重要性。希望这次可以坚持。转载于:https://www.cnblogs.com/cyforev…