volatile:

定义:Java编程语言允许线程访问共享变量,为了确保共享变量内被准确和一致性地更新,线程应该确保通过排它锁单独获得这个变量。根据volatile的定义,volatile有锁的语义。

作用:1.保证共享变量的可见性(这是volatile作为轻量级锁的基础);

    这里可见性的意思是:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值(与上篇定义的可见性有点区别啊,这里与上篇相比没有强调因重排序带来的有序性问题,进而导致的操作间可见性问题,也就是后面操作本来应该能看到前面操作结果的,结果因为重排序而看不到了,导致出现不可见性问题,笔者认为这本质上还是属于重排序问题。所以上篇定义的可见性,最后一句存在错误,但为了强调可见性与重排序(有序性)之间的区别,笔者并没有修改,当然也保留了这部分的解释)。

   2.禁止重排序(根据happens-before规则)

实现原理:

 1.保证共享变量可见性的实现原理(以X86处理器来分析):

  instance = new Single();//instance是volatile变量

  转换成汇编代码,如下:

  0x01a3deld: movb $0x0,0x1104800(%esi);0x01aa3de24:lock addl $0x0,(%esp);

  有volatile修饰的共享变量进行写操作的时候会多出第二行(带lock前缀的)汇编代码。

  Lock前缀的指令在多核处理器下的作用:

  1.将当前处理器的缓冲行的数据写会到系统内存;

  Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理环境中,LOCL#信号确保会在声言该信号期间,处理器可以独占任何共享内存。(采取机制有锁总线,锁缓存,目前的处理大多选择后者,因为锁总线的开销太大)。  

  对于目前的处理器,如果访问的区域已经缓存在处理器内部,则不会声言LOCK#信号,处理器会锁定这块内存的缓存并刷新到内存,并使用缓存一致性机制来确保修改的原子性,此操作称为“缓存锁定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。同时,Lock前缀指令会引起处理器缓存刷新到内存。

  2.这个写内存的操作会使在其它CPU里缓存了该内存地址的数据无效。

  实现机制:一个处理器的缓存刷新到内存会导致其它处理器的缓存无效。处理器使用嗅探技术来保证它的内部缓存,系统内存和其他处理器的缓存的数据在总线上保持一致。

  缓存一致性协议:在多处理下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址(中的数据)被修改,就会将当前处理器的缓存行设置为无效状态,当处理器对这个数据进行操作的时候,会重新从系统内存把数据读到处理器缓存里(看到这里是不是明白了只对volatile变量的写操作进行了处理(增加lock前缀),因为处理器缓存里的数据有么有效,有效就是最新值,要么无效,需要到主内存去读取)。

现在总结一下LOCK前缀指令的作用:

1.确保处理器修改共享数据的原子性(此时修改的值还在缓存),通过锁总线,锁缓存实现的

2.将修改后的值刷新到内存,同时刷新内存的这个动态,会被其他处理器嗅探到,然后其他处理器会将他们缓存里的数据置为无效状态。

2,volatile禁止重排序的实现原理

在讲解实现原理之前,我们先说一下volatile的内存语义:

内存语义的实现也需要可见性作为基础的。内存屏障只是禁止重排序的基础。

volatile写的内存语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值(所有的共享变量)刷新到主内存。

volatile读的内存语义:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效(本地内存中的所有数据都无效了),线程接下来将从主内存中读取数据。

volatile内存语义的实现:

volatitle重排序规则表:

从上表可知:

1、当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。

2、当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。

3、当第一个操作是volatile写时,第二个volatile操作是volatile读时,不能重排序。

通过volatile重排序的规则,编译器编译时会在这三种情况下插入相应的内存屏障,来保证volatile的内存语义。插入内存屏障的规则:

1、在每volatile写,之前插入StoreStore内存屏障指令,之后插入StoreLoad屏障;

2、在每volatile读,之后插入内存LoadLoad,LoadStore屏障指令。

这些内存屏障插入措施是非常保守的。因为有些内存屏障可以根据内存访问的具体情况(比如只有一个volatile读时,所有的屏障都可以省略)是可以省略掉的。尤其是这些措施是独立于处理器平台之上的,不同的处理器重排序的规则都不一样。比如,X86处理器,只允许写/读重排序,这种情况下,上面的内存屏障插入规则只需要StoreLoad就可以了。

根据volatile禁止重排序的规则,以及内存屏障指令的作用,我们可以总结volatile的一个特性:

对volatile变量的单个读/写,可以看成是使用同一个锁对这些单个volatile读/写做了同步,正是如此,volatile被称为轻量级锁。在功能上,锁比volatile更强大;在可伸缩性和执行性能上,volatile更有优势。

不同的处理器进行重排序的规则也不相同,以X86处理器为例,它允许的重排序就只有写-读。

synchronized

final

Java并发之同步原语的更多相关文章

  1. java并发之同步辅助类CyclicBarrier和CountDownLatch

    CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门, ...

  2. Java并发之同步工具类

    1. CountDownlatch(计数器) 描述: 一个同步工具类,允许一个或多个线程等待其它线程完成操作 类图 通过指定的count值进行初始化,调用await方法的线程将被阻塞,直到count值 ...

  3. java并发之同步辅助类CountDownLatch

    CountDownLatch 含义: CountDownLatch可以理解为一个计数器在初始化时设置初始值,当一个线程需要等待某些操作先完成时,需要调用await()方法.这个方法让线程进入休眠状态直 ...

  4. java并发之同步辅助类(Semphore、CountDownLatch、CyclicBarrier、Phaser)

    线程同步辅助类,主要学习两点: 1.上述几种同步辅助类的作用以及常用的方法 2.适用场景,如果有适当的场景可以用到,那无疑是最好的 semaphore(seməˌfôr) 含义 信号量就是可以声明多把 ...

  5. JAVA并发之锁获取步骤及锁优化

    在另外的两篇文章中先后介绍了轻量级同步关键字volatile和重量级锁关键字synchronized,这两个关键字是Java语言中进行线程同步的基本方式(当然还有ReentrenLock等显式锁方式) ...

  6. 从同步原语看非阻塞同步以及Java中的应用

    非阻塞同步:基于冲突检测的乐观并发策略,通俗讲就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了,如果争用数据有冲突那就采用其他的补偿措施(最常见的就是不断重试直到成功),这种乐观的并发策略 ...

  7. java并发之线程同步(synchronized和锁机制)

    使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...

  8. java并发之固定对象与实例

    java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...

  9. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

随机推荐

  1. 怎么给kibana加上权限?

    更新:2016-05-20 09:36 通过向Elastic了解,他们目前开发的5.0版本支持更好的权限定制,粒度达到字段级别.他们预计今年就可以上线,如果你不是有一个旧版本的kibanba非要维护不 ...

  2. solidity事件详解

    很多同学对Solidity 中的Event有疑问,这篇文章就来详细的看看Solidity 中Event到底有什么用? 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊. ...

  3. es6从零学习(二):promise

    es6从零学习(二):promise 一:promise的由来 某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱.由此,Promise的概念就由社区 ...

  4. android4.3 Bluetooth分析之扫描分析

    android4.3中引入了蓝牙低能耗le(low energy),相应的也有一些方法/类.不过代码里,并没有找到初始调用的地方.所以这里还是先只分析下bt普通的扫描流程(类似android 4.2) ...

  5. iOS- iOS 和 Android 的后台推送原理各是什么?有什么区别?

    iOS 的推送iOS 在系统级别有一个推送服务程序使用 5223 端口.使用这个端口的协议源于 Jabber 后来发展为 XMPP ,被用于 Gtalk 等 IM 软件中.所以, iOS 的推送,可以 ...

  6. 异步执行任务SimpleAsyncTaskExecutor详解

    SimpleAsyncTaskExecutor 异步执行用户任务的SimpleAsyncTaskExecutor.每次执行客户提交给它的任务时,它会启动新的线程,并允许开发者控制并发线程的上限(con ...

  7. zoj 1298 Domino Effect (最短路径)

    Domino Effect Time Limit: 2 Seconds      Memory Limit: 65536 KB Did you know that you can use domino ...

  8. C# 面向对象——继承

    继承:代码文字结合理解 class Program { static void Main(string[] args) { //Student s = new Student(); //Driver ...

  9. Oracle-RAC原理

    Oracle-RAC原理 来源 https://blog.csdn.net/qq_34556414/article/details/79001267 单点数据库 VS RAC 单节点数据库,如果实例宕 ...

  10. [CQOI2014]数三角形 组合数 + 容斥 + gcd

    推导过程 : 组合数+容斥原理+gcd 正确做法是暴力的一种优化,ans=所有情况 - 平行坐标轴的三点共线 - 斜线三点共线 如果快速求斜线三点共线: 首先要知道一个结论,对于点(a,b) (x,y ...