Java并发之同步原语
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并发之同步原语的更多相关文章
- java并发之同步辅助类CyclicBarrier和CountDownLatch
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门, ...
- Java并发之同步工具类
1. CountDownlatch(计数器) 描述: 一个同步工具类,允许一个或多个线程等待其它线程完成操作 类图 通过指定的count值进行初始化,调用await方法的线程将被阻塞,直到count值 ...
- java并发之同步辅助类CountDownLatch
CountDownLatch 含义: CountDownLatch可以理解为一个计数器在初始化时设置初始值,当一个线程需要等待某些操作先完成时,需要调用await()方法.这个方法让线程进入休眠状态直 ...
- java并发之同步辅助类(Semphore、CountDownLatch、CyclicBarrier、Phaser)
线程同步辅助类,主要学习两点: 1.上述几种同步辅助类的作用以及常用的方法 2.适用场景,如果有适当的场景可以用到,那无疑是最好的 semaphore(seməˌfôr) 含义 信号量就是可以声明多把 ...
- JAVA并发之锁获取步骤及锁优化
在另外的两篇文章中先后介绍了轻量级同步关键字volatile和重量级锁关键字synchronized,这两个关键字是Java语言中进行线程同步的基本方式(当然还有ReentrenLock等显式锁方式) ...
- 从同步原语看非阻塞同步以及Java中的应用
非阻塞同步:基于冲突检测的乐观并发策略,通俗讲就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了,如果争用数据有冲突那就采用其他的补偿措施(最常见的就是不断重试直到成功),这种乐观的并发策略 ...
- java并发之线程同步(synchronized和锁机制)
使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...
- java并发之固定对象与实例
java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...
- 深入理解Java并发之synchronized实现原理
深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...
随机推荐
- 应用Response.Write实现带有进度条的多文件上传
前几天,写过一篇随笔“使用RESPONSE.WRITE实现在页面的生命周期中前后台的交互”.说是交互,实际上也主要是在ASP.NET的页面周期中 从后台利用RESPONSE.WRITE向前台即时的推送 ...
- var,let,const,三种申明变量的整理
javascript,正在慢慢变成一个工业级语言,势力慢慢渗透ios,安卓,后台 首先let,是局部变量,块级作用域:var全局的,const是常量,也就是只读的: 一行demo说明 for (var ...
- Advanced Fruits (最大公共子序列的路径打印)
The company "21st Century Fruits" has specialized in creating new sorts of fruits by trans ...
- Median Weight Bead(最短路—floyed传递闭包)
Description There are N beads which of the same shape and size, but with different weights. N is an ...
- Android 网络编程 API笔记 - java.net 包相关 接口 api
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- lintcode-186-最多有多少个点在一条直线上
186-最多有多少个点在一条直线上 给出二维平面上的n个点,求最多有多少点在同一条直线上. 样例 给出4个点:(1, 2), (3, 6), (0, 0), (1, 3). 一条直线上的点最多有3个. ...
- TCP系列30—窗口管理&流控—4、Cork算法
一.Cork算法概述 Cork算法与Nagle算法类似,也有人把Cork算法称呼为super-Nagle.Nagle算法提出的背景是网络因为大量小包小包而导致利用率低下产生网络拥塞,网络发生拥塞的时候 ...
- Thinkphp5的使用phpmailer实现发邮件功能(163邮箱)
Thinkphp5本身并没有实现发邮件的功能,至少据我所知. 本文利用网易邮箱作为发邮件的邮箱.作为发送邮件的前提是需要开启SMTP服务,打开网易邮件,点击设置按钮,如下图所示 勾选smtp服务 保存 ...
- intellij idea 如何将一个普通项目转换为maven项目
1.工程文件下新建文件pom.xml,并填写好内容. 2.在pom.xml 文件上右键 Add as Maven Project.
- Python运算符与编码
阅读目录 while 循环 运算符 编码的问题 单位转换 整数 布尔值 while 循环 在生活中,我们遇到过循环的事情吧?比如循环听歌.在程序中,也是存才的,这就是流程控制语句 while 1.基本 ...