JAVA锁和volatile的内存语义&volatile的使用场景
JAVA锁的内存语义
当线程释放锁时,JMM(Java Memory Model)会把该线程对应的本地内存中的共享变量刷新到主内存中。
当线程获取锁时,JMM会将该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
对比锁释放-读取的内存语义与volatile写-读的内存语义可以看出,锁释放与volatile写具有相同的内存语义;锁获取与volatile读具有相同的内存语义。
下面对锁释放和锁获取的内存语义做个总结。
- 线程1释放一个锁,实质上是线程1向接下来将要获取这个锁的某个线程发出了(线程1对共享变量所做修改的)消息。
- 线程2获取一个锁,实质上是线程2接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
- 线程1锁释放,随后线程2获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。
锁内存语义的实现
锁有很多种,但其基本原理都是差不多的。书上是以ReentrantLock中的公平锁与非公平锁作为案例分析,有兴趣的同学可以去阅读原籍和源码。现总结如下:
- 公平锁和非公平锁释放时,最后都要写一个volatile变量state。
- 公平锁获取是,首先会去读volatile变量。
- 非公平锁获取时,首先会用CAS更新volatile变量,这个操作同时具有volatile读和写的内存语义。
所以锁释放-获取的内存语义的实现至少有下面两种方式:
1)利用volatile变量的写-读所具有的内存语义。
2)利用CAS所附带的volatile读和volatile写的内存语义。
由此可知:并发包下的类的实现方式大部分都是基于这两种方式实现的。
参考 https://www.cnblogs.com/yuanfy008/p/9346925.html
1.volatile的内存语义与实现
1.1 volatile读写的内存语义
volatile读写的内存语义 与 锁获取/锁释放 的内存语义对应相同
这两条保证了volatile修饰变量的可见性。
那JMM如何能够保证volatile实现其内存语义的,简单来说就是通过内存屏障。如果看过volatile变量汇编后的指令代码就会在代码中发现一句:
lock add1 $0x0
它的简单含义就是要把工作内存中的共享变量值刷新到主内存中,相当于加入内存屏障。
1.2 volatile有序的内存语义
volatile的另一个特性是禁止指令重排序,这里的内存语义我们可以总结为:
volatile读之后 的操作不会被重排序到 volatile读之前
volatile写之前 的操作不会被重排序到 volatile写之后
先volatile写–后volatile读,不可重排序
JMM通过插入内存屏障来实现以上语义,实质上有四种内存屏障策略:
volatile写操作前插入StoreStore屏障
volatile写操作后插入StoreLoad屏障
volatile读操作前插入LoadLoad屏障
volatile读操作后插入LoadStore屏障
其中StoreLoad屏障是全能型屏障,可以完成其他3个屏障的功能。所以它被大部分CPU支持。不同CPU有着不同的重排序规则,但是这一套JMM屏障策略可以完成所有类型CPU下的volatile语义。例如对于x86的CPU,它本身只支持写-读操作的重排序,对读-读,读-写,写-写操作的重排序都不支持;那么我们只需要加入StoreLoad屏障来避免写-读操作的重排序即可实现volatile语义。
亲测:volatile修饰的变量,对其相关操作是不具有原子性的
eg:volatile修饰的共享变量a 的自增操作 a++,在多个线程中运行;最后a的值很有可能小于++的执行次数。(因为a++ 实际是 a=a+1,是加法和赋值两个操作,不是原子操作。volatile不保证原子性,所以出现了小于++执行次数的结果)
volatile修饰的共享变量 ArrayList<Object> list,添加元素的操作list.add(new Object()),在多个线程中运行;最后list.size()也很有可能小于add执行的次数。(原因同上,list.add也不是原子操作)
对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
volatile的不能完全取代synchronized的位置,只有在一些特殊的场景下,才能适用volatile。总的来说,必须同时满足下面两个条件volatile修饰的变量才能保证在并发环境的线程安全:
(1)对变量的写操作不依赖于当前值。 --待思考
(2)该变量没有包含在具有其他变量的不变式中。 --待思考
三种类型的指令重排序
为了提高性能,编译器和处理器可能会对指令做重排序。重排序可以分为三种:
(1)编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
(2)指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
(3)内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
参考 http://www.cnblogs.com/paddix/p/5374810.html
JAVA锁和volatile的内存语义&volatile的使用场景的更多相关文章
- volatile的内存语义与应用
volatile的内存语义 volatile的特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,堪称是使用同一个锁对这些单个读/写操作做了同步. 锁的happens-b ...
- Java并发编程原理与实战四十二:锁与volatile的内存语义
锁与volatile的内存语义 1.锁的内存语义 2.volatile内存语义 3.synchronized内存语义 4.Lock与synchronized的区别 5.ReentrantLock源码实 ...
- Java内存模型-volatile的内存语义
一 引言 听说在Java 5之前volatile关键字备受争议,所以本文也不讨论1.5版本之前的volatile.本文主要针对1.5后即JSR-133针对volatile做了强化后的了解. 二 vol ...
- Java中锁的实现与内存语义
目录 1. 概述 2. 锁的内存语义 3. 锁内存语义的实现 4. 总结 1. 概述 锁在实际使用时只是明白锁限制了并发访问, 但是锁是如何实现并发访问的, 同学们可能不太清楚, 下面这篇文章就来揭开 ...
- volatile的内存语义
volatile的特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,堪称是使用同一个锁对这些单个读/写操作做了同步. 锁的happens-before规则保证释放锁和获 ...
- 【java多线程系列】java中的volatile的内存语义
在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...
- Java-内存模型 final 和 volatile 的内存语义
前提:内存屏障 内存屏障(Memory Barrier)与内存栅栏(Memory Fence)是同一个概念. 用于阻止指令重排序.保证了特定操作的执行顺序和某些变量的内存可见性. JMM 内存屏障分为 ...
- volatile内存语义
全面理解Java内存模型(JMM)及volatile关键字 volatile的内存语义 Volatile读写所建立的happens-before关系Volatile读写的内存语义 锁: 获取和释放Vo ...
- 基础篇:深入JMM内存模型解析volatile、synchronized的内存语义
目录 1 java内存模型,JMM(JAVA Memory Model) 2 CPU高速缓存.MESI协议 3 指令重排序和内存屏障指令 4 happen-before原则 5 synchronize ...
随机推荐
- Go语言(IDEA下+Eclipse下)Hello World
第一步,去下载Go环境 然后安装即可. IDEA 先安装GO插件: ..点击Browse... ..搜索GO ..点击安装,安装完之后重启 ..重启完之后,New~(IDEA已经自动识别出系统中安装的 ...
- 在Linux中复制文件夹下的全部文件到另外文件夹
https://jingyan.baidu.com/article/656db918f83c0de380249c5a.html 在Linux系统中复制或拷贝文件我们可以用cp或者copy命令,但要对一 ...
- 安卓和java开发环境的安装
java配置安卓需要,sdk,jdk(java),adt,eclipse 对于jdk的配置和安装: 1 卸载原来的jdk,重新配置环境变量只需要:在系统变量中把新的jdkJ安装路径写到“JAVA_HO ...
- Luogu P2613 【模板】有理数取余
题目链接 \(Click\) \(Here\) 真心没啥东西,只要能\(Get\)到在数字输入的时候按位取模,以及除数也可以直接取模就可以了.(把每个数看做乘法原理和加法原理构造起来的即可.) #in ...
- bzoj1061 建图 + 最小费用流
https://www.lydsy.com/JudgeOnline/problem.php?id=106152 对于一个点对上多个点,不太容易建图的时候,考虑逆向思考 申奥成功后,布布经过不懈努力,终 ...
- 9、字符串转数字用int、因为input里面接受的全部是字符串
#!/user/bin/python# -*- coding:utf-8 -*-num = int(input('请输入数字:'))if num == 1: print(666)elif num == ...
- MyBatis-CURD
一.接口方法 /** * 删除.修改.添加操作都可以返回三种类型 * Integer.Long.Boolean */ public interface MyUserMapper { public My ...
- ArcGis Python脚本——ArcGIS 中使用的 Python 是什么版本
Python 编程语言用于自 9.0 起的各版本 ArcGIS 中,并被整合到 ArcMap 和 ArcGIS for Server 的自动安装中. ArcGIS 将在完整安装过程中安装下列 Pyth ...
- 自定义类使用泛型and方法使用泛型
使用泛型的自定义类,泛型可以使用任意的数据类型,在创建对象的时候确定是什么数据类型,创建对象的时候不使用泛型,那就默认是Object类型 格式: 使用泛型的自定义类 package cn.zhuobo ...
- HTML&CSS总结
HTML 如果把网页比作房子的话,那么HTML就是搭建房子的整体结构,CSS就是对房子进行装修,HTML主要涉及各种标签的使用,总结如下,需要补充的一点是行内标签与块级标签的区别 inline:在一行 ...