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 ...
随机推荐
- springmvc 学习笔记
@Autowired,@RequestMapping,@RequestParam 使用该注解,引入对象时, 可以省略setter getter.减少代码显示. @AutowiredSimService ...
- 彻底删除与安装MySQL
一.下载msi安装版:https://dev.mysql.com/downloads/mysql/ 二.安装前一定要彻底删除已有的MySQL:(从未安装过,忽略此步) 1.计算机处右键快捷菜单,管理, ...
- logistics回归简单应用(二)
警告:本文为小白入门学习笔记 网上下载的数据集链接:https://pan.baidu.com/s/1NwSXJOCzgihPFZfw3NfnfA 密码: jmwz 不知道这个数据集干什么用的,根据直 ...
- Luogu P3975 [TJOI2015]弦论
题目链接 \(Click\) \(Here\) 题目大意: 重复子串不算的第\(k\)大子串 重复子串计入的第\(k\)大子串 写法:后缀自动机. 和\(OI\) \(Wiki\)上介绍的写法不太一样 ...
- 2Servlet笔记
1.静态页面(html) 2.动态 用户可以输入数据,和页面交互. 3 BS和CS的区别 BS :Browser Server(WEB 服务器) CS : Client(qq tcp/ip协议) ...
- docker 基础之数据管理
数据卷 一.将本地默认目录挂载到docker容器内指定的目录 #将本地的目录挂在到docker容器内 docker run -it --name container-test -h CONTAINER ...
- Yarn常用命令总结
Yarn常用命令总结 1>.查看任务列表 [root@storage101 ~]# yarn application -list :: INFO client.RMProxy: Connecti ...
- cmd命令对java程序进行编译时出现:编码GBK的不可映射字符
原因:由于JDK是国际版的,在编译的时候,如果我们没有用-encoding参数指定JAVA源程序的编码格式,则java.exe首先获得我们才做系统默认采用的编码格式,也即在编译JAVA程序时,若我们不 ...
- JAVA核心技术I---JAVA基础知识(文件系统及java文件基本操作)
一:文件概述 文件系统是由OS(操作系统)管理的 文件系统和Java进程是平行的,是两套系统 文件系统是由文件夹和文件递归组合而成 文件目录分隔符 –Linux/Unix 用/隔开 –Windows用 ...
- VScode 1.13 gocode提示dial tcp 216.239.37.1:443: connectex: A connection attempt failed because the connected..
在将VScode升级至 1.13后让升级gocode,在升级时报出如下错误 D:\go_work\src>go get -u -v github.com/mdempsky/gocode gith ...