java dcl 失效解决_DCL失效原因和解决方案

Java内存模型    在了解Java的同步秘密之前,先来看看JMM(Java Memory Model)。

Java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。而且Java语言最大的特点就是废除了指针,把程序员从痛苦中解脱出来,不用再考虑内存使用和管理方面的问题。

可惜世事总不尽如人意,虽然JMM设计上方便了程序员,但是它增加了虚拟机的复杂程度,而且还导致某些编程技巧在Java语言中失效。

JMM主要是为了规定了线程和内存之间的一些关系。对Java程序员来说只需负责用synchronized同步关键字,其它诸如与线程/内存之间进行数据交换/同步等繁琐工作均由虚拟机负责完成。根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

线程若要对某变量进行操作,必须经过一系列步骤:首先从主存复制/刷新数据到工作内存,然后执行代码,进行引用/赋值操作,最后把变量内容写回Main Memory。Java语言规范(JLS)中对线程和主存互操作定义了6个行为,分别为load,save,read,write,assign和use,这些操作行为具有原子性,且相互依赖,有明确的调用先后顺序。具体的描述请参见JLS第17章。

我们在前面的章节介绍了synchronized的作用,现在,从JMM的角度来重新审视synchronized关键字。

假设某条线程执行一个synchronized代码段,其间对某变量进行操作,JVM会依次执行如下动作:

(1) 获取同步对象monitor (lock)

(2) 从主存复制变量到当前工作内存 (read and load)

(3) 执行代码,改变共享变量值 (use and assign)

(4) 用工作内存数据刷新主存相关内容 (store and write)

(5) 释放同步对象锁 (unlock)

可见,synchronized的另外一个作用是保证主存内容和线程的工作内存中的数据的一致性。如果没有使用synchronized关键字,JVM不保证第2步和第4步会严格按照上述次序立即执行。因为根据JLS中的规定,线程的工作内存和主存之间的数据交换是松耦合的,什么时候需要刷新工作内存或者更新主内存内容,可以由具体的虚拟机实现自行决定。如果多个线程同时执行一段未经synchronized保护的代码段,很有可能某条线程已经改动了变量的值,但是其他线程却无法看到这个改动,依然在旧的变量值上进行运算,最终导致不可预料的运算结果。

DCL失效   针对延迟加载法的同步实现所产生的性能低的问题,我们可以采用DCL,即双重检查加锁(Double Check Lock)的方法来避免每次调用getInstance()方法时都同步。

在开始讨论之前,先介绍一下LazyLoad,这种技巧很常用,就是指一个类包含某个成员变量,在类初始化的时候并不立即为该变量初始化一个实例,而是等到真正要使用到该变量的时候才初始化之。

例如下面的代码:class Foo {

private Resource res = null;

public Resource getResource() {

if (res == null)

res = new Resource();

return res;

}

}   由于LazyLoad可以有效的减少系统资源消耗,提高程序整体的性能,所以被广泛的使用,连Java的缺省类加载器也采用这种方法来加载Java类。

在单线程环境下,一切都相安无事,但如果把上面的代码放到多线程环境下运行,那么就可能会出现问题。假设有2条线程,同时执行到了if(res == null),那么很有可能res被初始化2次,为了避免这样的Race Condition,得用synchronized关键字把上面的方法同步起来。代码如下:

Class Foo {

private Resource res = null;

public synchronized Resource getResource() {

if (res == null)

res = new Resource();

return res;

}

}   synchronized过的方法在速度上要比未同步的方法慢上100倍,同时你也发现,只有第一次调用该方法的时候才需要同步,而一旦res初始化完成,同步完全没必要。所以你很快就把代码重构成了下面的样子:

Class Foo {

private Resource res = null;

private Date d = new Data();

public Resource getResource() {

if (res == null){ //(1)

synchronized(Foo.class){

if(res == null){

res = new Resource(); //(2)

}

}

}

return res;

}

}Double-Checked Locking看起来是非常完美的。但是很遗憾,根据Java的语言规范,上面的代码是不可靠的。

出现上述问题, 最重要的2个原因如下:

1, 编译器优化了程序指令, 以加快cpu处理速度.

2, 多核cpu动态调整指令顺序, 以加快并行运算能力.

问题出现的顺序:

1, 线程A, 发现对象未实例化, 准备开始实例化

2, 由于编译器优化了程序指令, 允许对象在构造函数未调用完前, 将共享变量的引用指向部分构造的对象, 虽然对象未完全实例化, 但已经不为null了.

3, 线程B, 发现部分构造的对象已不是null, 则直接返回了该对象.无法保证语句(2)和语句(1)不存在happen-before(详解跳转)关系.

一个线程A运行到"这里"时,A的工作区中,肯定已经产生一个Foo对象,而且这时这个对象已经完成了Data d.现在线程A调用时间到,执行权被切换到另一个线程B来执行,会有什么问题呢?如果res不为null,线程B获得了一个res,但可能res.getD()却还没有初始化.

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

DCL解决方案1.DCL的替代 Initialize-On-Demand

Class ResSingleton {

public static Resource res = new Resource();

}

这里LazyFoo只有一个静态成员变量。当第一次使用ResSingleton.res的时候,JVM才会初始化一个Resource实例,并且JVM会保证初始化的结果及时写入主存,能让其他线程看到,这样就成功的实现了LazyLoad。

2、另外,可以将instance声明为volatile(详解跳转),即

private volatile static LazySingleton instance;

在读线程B读一个volatile变量后,写线程A在写这个volatile变量之前,所有可见的共享变量的值都将立即变得对读线程B可见。

参考:http://www.blogjava.net/weidagang2046/articles/3494.html

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

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

相关文章

李宁-2015年7月13日-个人文档

姓名 李宁 日期 2015年7月13日 主要工作及心得 由于我负责服务器端的编写工作,而各部分的客户端的操作都要与服务器端通信,所以在今天的调试中,我贯穿于各部分模块的调试和检测,主要负责在出现问题…

java.net.unknown_android -------- java.net.UnknownServiceException

最近升级了Android的API版本时 ,导致我的网络请求失败了,出现了这个错误 java.net.UnknownServiceException,这个错误,我在网上查到这个主要是由于,我们的OkHttp3会默认使用密文传输,而我们的代码中使用Htt…

无忧开通了博客园博客主页

无忧开通了博客园博客主页,今后在这里安家了。 分享一点工作经验和学习心得,有事没事常来看看。另一个独立博客www.wuyouseo.com 转载于:https://www.cnblogs.com/wuyoublog/p/4646481.html

pythonif语句的多分支使用_Python多分支if语句的使用

注意:if语句代码是从上往下执行的,当执行到满足条件的语句时,代码会停止往下执行注意:if语句后面要加上冒号score int (input("score:"))if score > 90:print("A")elif score > 80:print(&…

Visual Studio下Qt调用IDL

一.简单介绍: 1.ActiveQt包含QAxContainer和QAxServer组件。 1) QAxContainer允许使用COM对象,并且可以将ActiveX控件嵌入到Qt程序中去。 QAxContainer是有三个类组成的。分别是: QAxObject封装了COM对象 QAxWidget封装了ActiveX控…

安装java过程_Java的安装过程

记录一下自己在Windowns下安装java的过程打开网址后要先登录,如果没有号就先注册,然后才能下载step1:下载JDK(1)将鼠标指向download,会出现如下界面:(2)点击左上角PopularDownloads下的 Java for Developers进入如下界面&#xff…

HDU2571

早期昨晚,跪,体倦,简直太CF该。早上起来刷标题。Then,写python,shell,一天后基础。 标题或标题中国~!思维:本主题开始寻找一个dfs,但是,这个矩阵外观似太大,d…

dockerfile源码安装mysql_docker容器详解五: dockerfile实现tomcat环境以及源码安装mysql...

tomcat上一节讲到了dockerfile的基础,这一次咱们来作一个小的练习首先要了解tomcat安装的整个过程首先搭建 jdk环境:下载jdk包,解压以后添加环境变量而后搭建tomcat:下载tomcat包,解压,修改配置文件到一个工…

pom.xml的配置详解

<!--可以免费转载&#xff0c;转载时请注明出处 http://pengqb.iteye.com 。--><project xmlns"http://maven.apache.org/POM/4.0.0 " xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance " xsi:schemaLocation"http://maven.apache.or…

azkaban 与 java任务_azkaban任务报错java.lang.RuntimeException: The root scratch dir: /tmp/hive...

azkaban运行任务的时候失败报错如下&#xff1a;23-03-2016 08:16:14 CST analyzer-kafka2hdfs_new ERROR - Exception in thread "main" org.apache.hive.service.cli.HiveSQLException: java.lang.RuntimeException: The root scratch dir: /tmp/hive on HDFS shou…

php-fpm的重启/关闭

php 5.3.3 下的php-fpm 不再支持 php-fpm 以前具有的 /usr/local/php/sbin/php-fpm (start|stop|reload)等命令&#xff0c;需要使用信号控制&#xff1a; INT, TERM 立刻终止QUIT 平滑终止USR1 重新打开日志文件USR2 平滑重载所有worker进程并重新载入配置和二进制模块 kill -…

SQL server 2008数据库的备份与还原、分离(转)

一、SQL数据库的备份&#xff1a; 1、依次打开 开始菜单 → 程序 → Microsoft SQL Server 2008 → SQL Server Management Studio → 数据库&#xff1a;Dsideal_school_db既是我们需要备份的学籍数据库 图&#xff08;1&#xff09; 2、选择要备份的数据库“Dsideal_school_d…

Java做一个动画效果音量调节_设计与实现一个 ISoundable 接口,该接口具有发声功能、还能调节音量大小...

[java]代码库package experiment6;public interface ISoundable {public void increaseVolume();public void decreaseVolume();public void stopSound();public void playSound();}package experiment6;public class Radio implements ISoundable {public void increaseVolume…

人人都有极客精神

http://www.jisuanke.com/minicourse/59/438 人人公司是一家极为鼓励极客精神的公司&#xff0c;当有重要的项目需要上线但又时间太紧&#xff0c;甚至需要当天上线的时候&#xff0c;往往会挂起海盗旗开启电子日期显示&#xff0c;让大家可以在对时间有更明确的感知的情况下&a…

WPF入门教程系列十三——依赖属性(三)

四、 只读依赖属性 在以前在对于非WPF的功能来说&#xff0c;对于类的属性的封装中&#xff0c;经常会对那些希望暴露给外界只读操作的字段封装成只读属性&#xff0c;同样在WPF中也提供了只读属性的概念&#xff0c;如一些 WPF控件的依赖属性是只读的&#xff0c;它们经常用于…

java 反编译 类名_java javassist创建类和反编译类

public class Byte {public static void main(String[] args) throws Exception {//获得类池ClassPool poolClassPool.getDefault();//创建类CtClass ccpool.makeClass("cn.sxt.in.Emp");//创建属性CtField f1 CtField.make("private int empno;", cc);Ct…

java生成动态验证码_动态生成验证码案例

servlet代码package cn.guizimo.web.servlet;import javax.imageio.ImageIO;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.…

TCP、UDP、HTTP、SOCKET之间的区别

http://blog.csdn.net/magister_feng/article/details/8634518 转载于:https://www.cnblogs.com/luningning0901/p/4667733.html

特征选择算法java实现_relief算法特征选择

1.[文件]Relief算法程序.txt ~ 6KB下载(44)package com.relief.algorithm;import java.util.Random;import java.util.Set;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;public class Relie…

freemarker跳出循环

break语句跳出当前循环&#xff0c;如下: <#list table.columns as c> <#if c.isPK> <#assign pkNamec.columnName?cap_first /> <#break> </#if> </#list>转载于…