精通Java中的volatile关键字
在一些开源的框架的源码当中时不时都可以看到volatile这个关键字,最近特意学习一下volatile关键字的使用方法。
很多资料中是这样介绍volatile关键字的:
volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
文字不太好理解,通过例子来理解。
1、例子
首先看一个没有使用volatile关键字例子:
package com.swnote.java;
/**
* volatile测试例子
*
* @author lzj
* @date [2019-04-47]
*/
public class VolatileTest {
private boolean flag;
public static void main(String[] args) {
VolatileTest test = new VolatileTest();
test.test();
}
public void test() {
new Thread(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
}).start();
new Thread(() -> {
while (true) {
if (flag) {
System.out.println("thread flag = " + flag);
}
}
}).start();
}
}
该例子中定义了一个flag共享变量,test方法里面开启了两个线程,第一个线程在等待1秒中后修改共享变量flag的值为true,第二个线程通过循环判断flag的值,当flag的值为true时,输出内容。
此时有两种猜想:
- 执行后,可以看到输出内容,即说明第二个线程能够感知到第一个线程对共享变量flag的修改
- 执行后,没有任务内容,即说明第二个线程无法感知到第一个线程对共享变量flag的修改
然后执行结果为:

没有任务的输出内容,即证明了此时这样情况下第二个线程无法感知到第一个线程对共享变量flag的修改的
现在修改一下例子,即为flag变量加上volatile关键字,即:
private volatile boolean flag;
然后再运行,此时结果为:

此时就有内容输出了,说明加上volatile关键字后,第二个线程可以感知到第一个线程对共享变量flag的修改的,这就是上面概念中所说的volatile在多处理器开发中保证了共享变量的“可见性”。
2、原理
通过上面的例子证明了volatile在多处理器开发中保证了共享变量的“可见性”,那它是怎么实现的呢?
这时就得介绍一下Java的内存模型了,首先看如下示意图:

Java内存模型是如上面所示的:
共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在自己的本地内存中进行,而不能直接读写主内存中的变量。
根据此理解,上述例子的在没有加volatile时的情况是这样的:
第一个线程从主内存中获取共享变量flag的值,此时值为false,将该值放到自己的本地内存中,然后对变量进行修改,将值改为true,此时也只是将本地内存中flag的值改为了true,此时还没有将值同步到主内存中,然后第二线程也是将共享变量flag的值放到自己的本地内存中,而此时flag的值还是为false,所以就是一直没有内容输出了。
然而加上volatile关键字后,第一个线程对flag的修改会强制刷新到主内存中去,同时还会导致其他线程中的本地内存的值会无效,需要重新到主内存获取,这样就保证了第一个线程对flag修改后,第二线程能够感知到。
3、注意点
volatile是轻量级的synchronized,但是它是不能够代替synchronized的,因为volatile只能保证原子性操作的安全,对于复合操作,volatile是不能保证线程安全的。
例如:
package com.swnote.java;
/**
* 复合操作例子
*
* @author lzj
* @date [2019-04-27]
*/
public class StatisticTest {
private volatile int num = 0;
public static void main(String[] args) {
StatisticTest test = new StatisticTest();
test.statistic();
}
public void statistic() {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
num++;
}).start();
}
System.out.println("num = " + num);
}
}
期望的运行结果是20,可是几乎每次运行结果都是不一样的,例如有的结果为:

这是因为num++这个操作不是原子性的,所以即使使用了volatile关键字,也是不能保证安全的。
关注我
以你最方便的方式关注我:
微信公众号:

精通Java中的volatile关键字的更多相关文章
- java中的volatile关键字
java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...
- Java中的volatile关键字的功能
Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...
- 深入理解Java中的volatile关键字
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- java中的Volatile关键字使用
文章目录 什么时候使用volatile Happens-Before java中的Volatile关键字使用 在本文中,我们会介绍java中的一个关键字volatile. volatile的中文意思是 ...
- Java中的volatile关键字为什么不是不具有原子性
Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值是原子操作,为什么?为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volat ...
- CPU缓存一致性协议与java中的volatile关键字
有关缓存一致性协议MESI自行百度. 提出问题:volatile在缓存一致性协议上又做了哪些事情?为啥它不保证原子性? 在缓存一致性协议下,CPU为了执行效率使用了写(存储)缓存和失效队列从而导致对用 ...
- zz剖析为什么在多核多线程程序中要慎用volatile关键字?
[摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...
- java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)
概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...
- 再议Java中的static关键字
再议Java中的static关键字 java中的static关键字在很久之前的一篇博文中已经讲到过了,感兴趣的朋友可以参考:<Java中的static关键字解析>. 今天我们再来谈一谈st ...
随机推荐
- (九)文档和视图,Invalidate,数据库编程
一.文档视图结构 文档类(CDocument):存储加载(读写)数据视图类(CView):显示和修改数据 1)单文档 a)文档模板:把框架窗口.文档.视图关联在一起b)文档类(CDocument): ...
- 【csp模拟赛2】 序列操作
线性推,开数组太麻烦,可以用指针 代码: #include <iostream> #include <cstdio> #include <queue> using ...
- Python 爬取喜马拉雅音频
一.分析音频下载相关链接地址 1. 分析专辑音频列表页面 在 PC端用 Chrome 浏览器中打开 喜马拉雅 网站,打开 Chrome开发者工具,随意打开一个音频专辑页面,Chrome开发者工具中 ...
- UVA 247 Calling Circles —— (强连通分量模板题)
第一个强连通分量的题. 题意:有一堆人,a给b打电话表示a有一条向b的边,一个强连通分量代表一个电话圈,把每个电话圈里的人在一行内输出出来. 直接上模板即可,但是要注意把string用map映射一下的 ...
- Tomcat配置 https SSL证书
HTTP与HTTPS的区别:HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Soc ...
- Ubuntu安装Redis及使用
NoSQL简介NoSQL,全名为Not Only SQL,指的是非关系型的数据库随着访问量的上升,网站的数据库性能出现了问题,于是nosql被设计出来 优点/缺点优点:高可扩展性分布式计算低成本架构的 ...
- Failed to execute aapt
Failed to execute aapt 没错,看到这个表示你的资源出错了.不用想别的. 比如: Failed to execute aapt com.android.ide.common.pro ...
- 如何修改Zend Studio代码字体和大小
Zend Studio的默认字体非常小,看起来很费神,这里教大家怎么修改它的字体和大小. 工具/原料 Zend Studio 方法/步骤 Window-->preferences ...
- Docker Registry使用记录
一.介绍 有时我们的服务器无法访问互联网,或者你不希望将自己的镜像放到公网当中,那么你就需要Docker Registry,它可以用来存储和管理自己的镜像,即私有镜像库. 二.使用 2.1 获取最新镜 ...
- idea设置包的导入和提示重复代码下波浪线
1.一般idea都不会导入包.即使按了(以下都是已eclipse设置idea的快捷键) alt+enter键也不能导入. 2.关闭重复代码提示(也就是重复代码有波浪线)