一、原子性

原子性操作指相应的操作是单一不可分割的操作。例如,对int变量count执行count++d操作就不是原子性操作。因为count++实际上可以分解为3个操作:(1)读取变量count的当前值;(2)拿count的当前值和1做加法运算;(3)将加完后的值赋给count变量。

在多线程环境中,非原子操作可能会受其他线程的干扰。比如,上述例子如果没有对相应的代码进行同步(Synchronization)处理,则可能出现在执行第2个操作的时候,count变量的值已经被其他线程修改过了。当然,synchronized关键字可以帮助我们实现原子性操作,以避免这种线程间的干扰情况。

synchronized关键字可以实现操作的原子性,其实质是:通过该关键字所包括的临界区(Critical Section)的排他性保证在任何一个时刻只有一个线程能够执行临界区中的代码,这使得临界区中的代码代表了一个原子操作。这一点,大家基本都很清楚。但是,synchronized关键字所起到的另一个作用——保证内存的可见性(Memory Visibility),也是我们值得回顾的地方。

二、内存可见性

CPU在执行代码的时候,为了减少变量访问的时间消耗可能将代码中访问的变量的值缓存到该CPU缓存区中,因此,相应的代码再次访问该变量的时候,相应的值可能从CPU缓存中而不是主内存中读取的。同样的,代码对这些被缓存过的变量的值的修改也可能仅是被写入CPU缓存区,而没有写入主内存。由于每个CPU都有自己的缓存区,因此一个CPU缓存区中的内容对于其他CPU而言是不可见的。这就导致了在其他CPU上运行的其他线程可能无法“看到”该线程对某个变量值的更改。这就是所谓的内存可见性。

synchronized关键字的另一个作用就是保证了一个线程执行临界区中的代码时,所修改的变量值对于稍后执行该临界区的线程来说是可见的。这对于保证多线程代码的正确性来说非常重要。

而volatile关键字也能够保证内存可见性。即一个线程对一个采用volatile关键字修饰的变量的值的更改,对于其他访问该变量的线程而言总是可见的。也就是说,其他线程不会读到一个“过期”的变量值。因此,有人将volatile关键字和synchronized关键字所代表的内部锁做比较,将其称为轻量级的锁。这种称呼其实并不恰当,volatile关键字只能保证内存可见性,它并不能像synchronized关键字所代表的内部锁那样能够保证操作的原子性。volatile关键字实现内存可见性的核心机制是:当一个线程修改了一个volatile修饰的变量的值时,该值会被写入主内存(即RAM)而不仅仅是当前线程所在的CPU的缓存区,而其他CPU的缓存区中存储的该变量的值也会因此而失效(从而得以更新为主内存中该变量的“新值”)。这就保证了其他线程访问该volatile修饰的变量时,总是可以获取到该变量的最新值。

三、指令重排序

volatile关键字的另一个作用是:它禁止了指令重排序(Re-order)。编译器和CPU为了提高指令的执行效率可能会进行指令重排序,这使得代码的实际执行方式可能不是按照我们所认为的方式进行。例如下面的实例变量初始化语句:

private SomeClass someObject = new SomeClass();

上述语句非常简单:(1)创建类SomeClass 的实例;(2)将类SomeClass 的实例的引用赋给变量someObject 。但是由于指令的重排序作用,这段代码的实际执行顺序可能是:(1)分配一段用于存储SomeClass 实例的内存空间;(2)将对该内存空间的引用赋给变量someObject;(3)创建类SomeClass 的实例。因此,当其他线程访问someObject变量的值时,其得到的仅是指向一段存储SomeClass 实例的的内存空间的引用而已,而该内存空间相应的SomeClass 实例的初始化可能尚未完成,这就可能导致一些意想不到的结果。而禁止指令重排序则是可以使得上述代码按照我们所期望的顺序(正如代码所表达的顺序)来执行。

禁止指令重排序虽然导致编译器和CPU无法对一些指令进行可能的优化,但是它某种程度上让代码执行看起来更符合我们的期望。

四、Volatile、synchronized两者的区别联系

1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
3.volatile仅能实现变量的修改可见性,不能保证原子性(线程A修改了变量还没结束时,另外的线程B可以看到已修改的值,而且可以修改这个变量,而不用等待A释放锁,因为Volatile 变量没上锁);而synchronized则可以保证变量的修改可见性和原子性。
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞和上下文切换。
5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

文章摘写自:https://blog.csdn.net/ljheee/article/details/53152737

原子性、内存可见性和重排序——重新认识synchronized和volatile的更多相关文章

  1. Java内存模型(三)原子性、内存可见性、重排序、顺序一致性、volatile、锁、final

          一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量co ...

  2. volotile关键字的内存可见性及重排序

    在理解volotile关键字的作用之前,先粗略解释下内存可见性与指令重排序. 1. 内存可见性 Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存,并且线程 ...

  3. Java内存模型_重排序

    重排序:是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段 1..编译器优化的重排序.编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序. 2..指令级并行的重排序.现 ...

  4. 《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before

    第16章 Java内存模型 终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻.这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略 ...

  5. 【死磕Java并发】-----Java内存模型之重排序

    在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件: 在单线程环境下不能改变程序运行的结果: 存在数据依赖关系的不 ...

  6. Java内存模型之重排序

    参考链接:https://blog.csdn.net/huzhigenlaohu/article/details/51595676

  7. java内存模型——重排序

    线程安全问题概括来说表现为三个方面:原子性,可见性和有序性. 在多核处理器的环境下:编译器可能改变两个操作的先后顺序:处理器可能不是完全依照程序的目标代码所指定的顺序执行命令:一个处理器执行的多个操作 ...

  8. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...

  9. JMM中的重排序及内存屏障

    目录 1. 概述 2. 重排序 2-1. as-if-serial语义 2-2. 重排序的种类 2-3. 从Java源代码到最终实际执行的指令序列, 会分别经历下面3中重排序. 3. 内存屏障类型 3 ...

随机推荐

  1. python与其他语言的区别

    C 和 Python.Java.C#等 C语言: 代码编译得到 机器码 ,机器码在处理器上直接执行,每一条指令控制CPU工作 其他语言: 代码编译得到 字节码 ,虚拟机执行字节码并转换成机器码再后在处 ...

  2. django中的路由控制详解

    一 Django中路由的作用 二 简单的路由配置 三 有名分组 四 路由分发 五 反向解析 六 名称空间 七 django2.0版的path 一 Django中路由的作用 URL配置(URLconf) ...

  3. 八月暑期福利,10本Python热门书籍免费送!

    八月第一周,网易云社区联合博文视点为大家带来Python专场送书福利,10本关于Python的书籍内容涉及Python入门.绝技.开发.数据分析.深度学习.量化投资等.以下为书籍简介,送书福利请见文末 ...

  4. 搞懂.NET Framework 历史版本(2017年)

    最近被.NET平台各种名词.以及各种版本弄得有些疑惑,开发和部署,对于开发平台版本选择是个基本问题,因此,花了些时间,学习汇总了有关.NET版本演进的历史. .NET简介 这个平台相信我们都知道,不过 ...

  5. 性能测试工具——LoadRunner篇(一)

    一.LoadRunner组件 1.Virtual User Generato——r录制最终用户业务流程并创建性能 2.Controller——组织.驱动.管理并发监控负载测试 3.Analysis—— ...

  6. EXE模块说明

    EXE模块是fastCMS系统内非常优秀的一个功能模块,它将一些操作打包成可执行单元.它具有以下优势: 1)功能明确.便于维护. 2)发起端可获取EXE模块的执行结果. 3)对于不需要执行结果的请求, ...

  7. PHPCMS的产品筛选功能

    如下图所示功能: 首先,用下面这些代码替换掉phpcms/libs/functions/extention.func.php的内容 <?php /** * extention.func.php ...

  8. CentOS7部署ELK5.2

    原文发表于cu:2017-02-10 参考文档: Elasticsearchyum文档:https://www.elastic.co/guide/en/elasticsearch/reference/ ...

  9. spring-boot-mybatis搭建

    写在开始 mybatis是一个持久化框架,支持手动sql.存储过程.高级映射.mybatis支持XML方式或注解方式将POJO与数据库表间建立映射. maven依赖 spring-boot.mysql ...

  10. Spring Bean注册解析(一)

           Spring是通过IoC容器对Bean进行管理的,而Bean的初始化主要分为两个过程:Bean的注册和Bean实例化.Bean的注册主要是指Spring通过读取配置文件获取各个bean的 ...