原子性、内存可见性和重排序——重新认识synchronized和volatile
一、原子性
原子性操作指相应的操作是单一不可分割的操作。例如,对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的更多相关文章
- Java内存模型(三)原子性、内存可见性、重排序、顺序一致性、volatile、锁、final
一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量co ...
- volotile关键字的内存可见性及重排序
在理解volotile关键字的作用之前,先粗略解释下内存可见性与指令重排序. 1. 内存可见性 Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存,并且线程 ...
- Java内存模型_重排序
重排序:是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段 1..编译器优化的重排序.编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序. 2..指令级并行的重排序.现 ...
- 《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before
第16章 Java内存模型 终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻.这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略 ...
- 【死磕Java并发】-----Java内存模型之重排序
在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件: 在单线程环境下不能改变程序运行的结果: 存在数据依赖关系的不 ...
- Java内存模型之重排序
参考链接:https://blog.csdn.net/huzhigenlaohu/article/details/51595676
- java内存模型——重排序
线程安全问题概括来说表现为三个方面:原子性,可见性和有序性. 在多核处理器的环境下:编译器可能改变两个操作的先后顺序:处理器可能不是完全依照程序的目标代码所指定的顺序执行命令:一个处理器执行的多个操作 ...
- 【java多线程系列】java内存模型与指令重排序
在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...
- JMM中的重排序及内存屏障
目录 1. 概述 2. 重排序 2-1. as-if-serial语义 2-2. 重排序的种类 2-3. 从Java源代码到最终实际执行的指令序列, 会分别经历下面3中重排序. 3. 内存屏障类型 3 ...
随机推荐
- itop4412学习-超级块操作
1. 先看下超级块支持的函数列表,文件路径\4412_SCP精英版\Android源码\iTop4412_Kernel_3.0_20140521\iTop4412_Kernel_3.0\include ...
- javaweb(三十九)——数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- C#与mongoDB初始环境搭建
mongoDB官网https://www.mongodb.com/ mongoDB默认安装路径(Windows x64平台) C:\Program Files\MongoDB\Server\3.4\b ...
- web _service 接口
1.WebService 就是 http请求 post接口 2.需要加 请求头信息 Content-Type: text/xml; 3.需要把占位符换成需要的字符串 webservice接口可以 ...
- website for personal research
YOLO https://pjreddie.com/darknet/yolo/ Low Rank Matrix Recovery and Completion via Convex Optimizat ...
- Amazon.com 购物 信用卡预售期
I understand and thanks for confirming. In this case, the $1.00 is not a charge. It is an authoriza ...
- 美国警察iPhone数据线挡住歹徒子弹获救
泡泡网手机频道11月1日 现在手机的功能越来越丰富,不仅可以接打电话.收发短信.玩游戏聊天,关键时刻还能救命.前天HTC手机再次忠心护主,让许多同学对HTC赞赏有加.而现在又有人捡了一条命,不过这次救 ...
- 你用 Python 做过什么有趣的数据挖掘项目?
有网友在知乎提问:「你用 Python 做过什么有趣的数据挖掘项目?」 我最近刚开始学习 Python, numpy, scipy 等, 想做一些数据方面的项目,但是之前又没有这方面的经验.所以想知道 ...
- mongodb redis memcache 对比
从以下几个维度,对 Redis.memcache.MongoDB 做了对比. 1.性能 都比较高,性能对我们来说应该都不是瓶颈. 总体来讲,TPS 方面 redis 和 memcache 差不多,要大 ...
- javascript event对象操作
js代码: $(".leads_detail").click(function(e){ e = e || event; var t = e.target || e.srcEleme ...