java volatile关键字作用及使用场景
1. volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。如以下代码片段,isShutDown被置为true后,doWork方法仍有执行。如用volatile修饰isShutDown变量,可避免此问题。
public class VolatileTest3 {
static class Work {
boolean isShutDown = false;
void shutdown() {
isShutDown = true;
System.out.println("shutdown!");
}
void doWork() {
while (!isShutDown) {
System.out.println("doWork");
}
}
}
public static void main(String[] args) {
Work work = new Work();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::shutdown).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
new Thread(work::doWork).start();
}
}
出现脏读时,运行结果如下:

2. 为什么会出现脏读?
Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。变量的值何时从线程的工作内存写回主存,无法确定。
3. happens-before规则的理解与勘误
在网上查volatile关键字相关信息时,多篇博客提到了happens-before原则,个人对此原则的理解是:当操作该volatile变量时,所有前序对该变量的操作都已完成(如不存在已变更,但未写回主存的情况),所有后续对该变量的操作,都未开始。仅此而已。
这里,我认为网上很常见的一个理论对此理解有误,如下图。此观点认为,由于volatile变量flag的happens-before原则,所以A线程2处对其的写操作一定先于B线程3处对其的读操作。其实这种观点是有逻辑缺陷的,如果存在一个C线程,先读取flag的值,后写入flag的值,那C线程的执行时机是什么呢?如果还有其他D、E线程呢。。。对于这段代码的正确理解是,只要3处拿到的flag是true,那么a的值一定是1,而不是0.因为volition修饰的变量,处理器不会对其进行重排序,所以1处对a的赋值,一定发生在2处对flag的赋值之前。如果flag不是volatile变量,那么1处和2处代码的执行顺序是无法保证的(处理器的指令重排序),虽然大部分情况1会先于2执行。happens-before原则约束的并不是多线程对同一变量的读和写操作之间的顺序,而是保证读操作时,前序所有对该变量的写操作已生效(写回主存)。

验证如下:
public class VolatileTest {
static class A {
int a = 0;
volatile boolean flag = false;
void writer() {
a = 1; //
flag = true; //
System.out.println("write");
}
void reader() {
if (flag) { //
int i = a; //
System.out.println("read true");
System.out.println("i is :" + i);
} else {
int i = a;
System.out.println("read false");
System.out.println("i is :" + i);
}
}
}
public static void main(String[] args) {
A aaa = new A();
new Thread(() -> aaa.reader()).start();
new Thread(() -> aaa.writer()).start();
}
}
运行结果如下,在写操作执行之前,读操作已完成

4. volatile关键字使用场景
注意:volatile只能保证变量的可见性,不能保证对volatile变量操作的原子性,见如下代码:
public class VolatileTest2 {
static class A {
volatile int a = 0;
void increase() {
a++;
}
int getA(){
return a;
}
}
public static void main(String[] args) {
A a = new A();
new Thread(() -> {
for (int i = 0;i < 1000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 2000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 3000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 4000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
new Thread(() -> {
for (int i = 0;i < 5000;i++) {
a.increase();
}
System.out.println(a.getA());
}).start();
}
}
运行结果如下,volatile无法保证a++操作的原子性。

volatile正确的使用方法可参考:https://blog.csdn.net/vking_wang/article/details/9982709
java volatile关键字作用及使用场景的更多相关文章
- java transient关键字作用,使用场景。
java transient关键字作用,使用场景. 2016年08月31日 15:31:10 阅读数:4280 transient的作用及使用方法,官方解释为: Variables may be ma ...
- java transient关键字作用,使用场景
transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...
- Java volatile关键字详解
Java volatile关键字详解 volatile是java中的一个关键字,用于修饰变量.被此关键修饰的变量可以禁止对此变量操作的指令进行重排,还有保持内存的可见性. 简言之它的作用就是: 禁止指 ...
- [Java并发编程(三)] Java volatile 关键字介绍
[Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...
- Java volatile关键字解惑
volatile特性 内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的. volatile的使用场景 通过关 ...
- Java Volatile关键字(转)
出处: Java Volatile关键字 Java的volatile关键字用于标记一个变量“应当存储在主存”.更确切地说,每次读取volatile变量,都应该从主存读取,而不是从CPU缓存读取.每次 ...
- Java volatile 关键字底层实现原理解析
本文转载自Java volatile 关键字底层实现原理解析 导语 在Java多线程并发编程中,volatile关键词扮演着重要角色,它是轻量级的synchronized,在多处理器开发中保证了共享变 ...
- 13、Java并发性和多线程-Java Volatile关键字
以下内容转自http://tutorials.jenkov.com/java-concurrency/volatile.html(使用谷歌翻译): Java volatile关键字用于将Java变量标 ...
- 对Java单例模式 volatile关键字作用的理解
单例模式是程序设计中经常用到的,简单便捷的设计模式,也是很多程序猿对设计模式入门的第一节课.其中最经典的一种写法是: class Singleton { private volatile static ...
随机推荐
- 【对象属性复制】BeanUtils.copyProperties(obj1, obj2);
实现对象的属性值复制,只会复制命名相同的文件. import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(obj1, o ...
- Python开发【第九篇】: 并发编程
内容概要 操作系统介绍 进程 线程 协程 二. 进程 python并发编程之多进程理论部分 在python程序中的进程操作 运行中的程序就是一个进程.所有的进程都是通过它的父进程来创建的.因此,运行起 ...
- Spring中的配置文件文件位置
在Java开发中,一般在Spring框架中,classpath的位置是指src下,在IDEA中一般是指resource中 配置文件 位置:任意,开发中一般在classpath下(src) 名称:任意, ...
- Gym 101257B:2Trees(DFS+思维)
http://codeforces.com/gym/101257/problem/B 题意:给出两棵叶子数一样的树,在将叶子合并之后,对这个图进行染色,相邻的结点颜色不能相同,问最少需要染的颜色数,并 ...
- Python 爬虫从入门到进阶之路(十六)
之前的文章我们介绍了几种可以爬取网站信息的模块,并根据这些模块爬取了<糗事百科>的糗百内容,本章我们来看一下用于专门爬取网站信息的框架 Scrapy. Scrapy是用纯Python实现一 ...
- jacoco生成覆盖率报告
操作步骤: 1.下载git上最新的代码到本地 git clone {代码地址} 2.在服务器上打出相关服务的jar包 1) 登上服务器,切换到目标服务所在路径: cd /xx/xx/xx/xx 2) ...
- django实战仿慕课网在线视频网站(完成了85%以上的功能已上传github:https://github.com/huwei86/mxonline):
1. 前台功能模块 基本模块:登录 注册 找回密码 / 全局搜索 / 个人中心, 课程功能:课程管理 / 讲师管理 / 授课机构管理 热门推荐 相关课程推荐 用户操作管理:用户收藏 / 课程评论 ...
- Oracle数据库---PLSQL
SET SERVEROUTPUT ONBEGIN --打印输出 DBMS_OUTPUT.PUT_LINE('hello everyone!');END; DECLARE v_name VARCHAR2 ...
- 自己实现定制自己的专属java锁,来高效规避不稳定的第三方
java juc 包下面已经提供了很多并发锁工具供我们使用,但在日常开发中,为了各种原因我们总是会用多线程来并发处理一些问题,然而并不是所有的场景都可以使用juc 或者java本身提供的锁来方便的帮助 ...
- 百度小程序自定义通用toast组件
百度小程序Toast组件 author: @TiffanysBear 百度小程序自定义通用toast组件 BdToast百度小程序自定义通用组件-github地址 需求 手百小程序的toast仅支持在 ...