高并发编程-volatile详解

主内存与工作内存

在介绍volatile之前,先简单了解一下Java内存模型。在Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽各个硬件平台和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果,笔者认为是定义了程序中变量的访问规则。

主内存

所有线程共享的区域,存储线程共享的数据,包括实例变量、静态变量和构成数组的对象的元素,不包括局部变量和方法参数。

工作内存

每个线程独享的区域,存储主内存中的数据拷贝。

Java内存模型规定所有的变量都是存在主内存中,每个线程都有自己的工作内存。每个线程对共享数据的读、写操作都只在各自的工作内存中,不能直接在主内存中进行;在各自工作内存中对数据操作完成后,同步到主内存中;线程间不能访问各自工作内存中的数据,只能通过主内存来完成。下面用一张图来展示线程、主内存和工作内存三者之间的交互关系。

volatile

当使用volatile关键字修饰共享变量(实例变量、静态变量)时,它将具备两个特性:可见性和禁止指令重排序优化。

可见性

变量被修改后的新值会立即写回主内存中,同时会使其它线程工作内存中的旧值失效,新值对其它线程来说是可见的,其它线程可以立即得到修改后的新值,因为volatile变量每次使用之前都需要从主内存刷新获取最新值。

关于volatile实现的可见性可能会误解,认为既然volatile变量所有的写操作都会立刻反应到其它线程中,那么对volatile变量进行并发操作就是安全的。有这个误解是因为忽略了原子性,volatile是不保证原子性的。对一个变量进行修改赋值操作,可能写的就是一条简单的i=i+1,但是底层实现上会需要多条字节码指令来完成,同时一条字节码指令也可能转化成多条机器码指令,在并发情况下,这些指令的执行不能保证原子性。

禁止指令重排序优化

指令重排序是指CPU在正确处理指令依赖(数据依赖)并且保障程序执行得到正确结果的情况下,调整代码的执行顺序,允许将多条指令不按照程序规定顺序分开发送给各相应电路单元处理。需要注意的是指令重排序不会影响到代码在单线程环境下的执行,会影响到多线程并发情况下执行的正确性。

volatile禁止指令重排序是通过lock前缀指令实现的,lock前缀的指令相当于一个内存屏障,指令重排序时不能把后面的指令重排序到lock前缀指令之前,同时它会强制将对工作内存的修改操作立即写入主内存中。

使用条件

虽然volatile可以实现最轻量级的同步机制,但是使用volatile修饰的变量必须满足以下两个条件:

  • 对变量的写操作不依赖于当前值,或者确保只有一个线程修改变量的值;
  • 该变量没有包含在具有其他变量的不变式中。

原文发布于微信公众号 - JavaQ(Java-Q)

原文发表时间:2018-11-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python小屋

使用Python检查密码安全程度

本文主要演示几种内置用法的用法和代码优化技巧,所以没有使用正则表达式。 import string def check(pwd): #密码必须至少包含6个字符...

35150
来自专栏菩提树下的杨过

利用ganymed-ssh2远程执行其它Linux机器上的shell命令

实际应用中,有时候需要从web管理界面上,远程去启动其它linux主机上的程序,利用ssh协议可以方便的满足这一需求。事实上hadoop架构中,从nn上启动dn...

306100
来自专栏Astropeak

Spring使用 --- 基本概念(一):DI,依赖注入

12520
来自专栏python3

习题31:访问列表元素

访问列表中的元素,使用下标的方式,通常以0开始(为什么是0而不是1),这里程序的设计就是如此,个人觉得没有必要纠结,如有兴趣,可自行查看资料

8920
来自专栏Java架构沉思录

聊聊Java动态代理(下)

前言 在之前的文章《聊聊Java动态代理(上)》中,笔者为大家介绍了Java原生的动态代理,并指出Java原生的动态代理有一个缺点就是被代理类必须显示地实现某个...

31290
来自专栏帮你学MatLab

《Experiment with MATLAB》读书笔记(一)

读书笔记(一) 这是第一部分——迭代 将代码复制到m文件中即可运行 % 迭代是计算的关键 % % 上键:调用先前的命令 % %下面这个“双%”表示一个se...

29880
来自专栏DT乱“码”

python处理get请求和post请求

#处理get请求,不传data,则为get请求 import urllib from urllib.request import urlopen from u...

492100
来自专栏数据结构与算法

7617:输出前k大的数

7617:输出前k大的数 查看 提交 统计 提问 总时间限制:10000ms单个测试点时间限制:1000ms内存限制:65536kB描述 给定一个数组,统计前k...

40550
来自专栏nummy

__import__详解

当使用import导入Python模块的时候,默认调用的是__import__()函数。直接使用该函数的情况很少见,一般用于动态加载模块。

8920
来自专栏JAVA技术zhai

干货:Java多线程详解(内附源码)

38140

扫码关注云+社区

领取腾讯云代金券