volatile关键字是如何起作用的?
关键字volatile是Java虚拟机提供的最轻量级的同步机制,但是在平时的项目里面,遇到需要多线程的时候更多地使用的是synchronized关键字来进行同步。个人而言,更多的原因是对volatile关键字的机制不了解导致的。
Java内存模型对volatile专门定义了一些特殊的访问规则,当一个变量定义为volatile之后便具有了两种特性:
1. 保证此变量对所有线程的可见性,“可见性”指当一条线程修改了这个变量的值,新的值对与其他线程来说是立即得知的。
2. 禁止指令重排序优化。
接下来将对上述两个方面分别介绍:
普通变量的值在县城之间传递均需要通过主内存来完成,例如,线程A修改一个普通变量的值,然后向内存进行会写,另外一条线程B在线程A回写完之后再从主内存中进行读取操作,新变量值才会对线程B可见。
尽管volatile定义的变量对所有的线程都是可见的,但是并不能说明volatile定义的变量的运算在并发下就是安全的。
(在各个线程的工作内存中,volatile变量也可以存在不一致的情况,但是由于每次使用之前都已经刷新了,执行引擎看不到不一致的情况,所以便认为不存在不一致的情况)
导致不安全的原因其实还是Java运算是非原子操作。所谓原子操作是指操作的执行不会被线程的调度给打断。
可以看这个例子:
public class VolatileTest {
public static volatile int race = 0; public static void increase(){
race++;
} private static final int THREAD_COUNT = 20; public static void main(String[] args) {
Thread[] threads = new Thread[THREAD_COUNT];
for(int i = 0; i < THREAD_COUNT; i++){
threads[i] = new Thread(new Runnable() { @Override
public void run() {
for(int i = 0; i < 1000; i++){
increase();
}
}
});
threads[i].start();
}
while(Thread.activeCount() > 1){
Thread.yield();
}
System.out.println(race);
}
}
20个线程,每个线程会对race变量进行1000次自增操作,即race++。输出的结果应该是20000,但是会发现每次运行的结果都不一样,而且都是一个小于20000的数。
导致的原因是race++操作并不是一个原子操作,尽管看来它只有一句话,但是在编译时并不是这样的。用javap命名进行反编译,同时输出附加信息:
increase()方法的执行一共是四条字节码指令完成的,熟悉字节码命令的可以看出着四步的操作,
当getstatic指令将race的值取到操作栈的时候,volatile保证了race的值是正确的,但是在执行后面的指令的时候,其它的线程可能已经把race的值修改了。
即使编译出来的只有一条字节码指令,但也不意味这是一个原子操作。
由于volatile变量智能保证可见性,依然需要synchronized和java.util.concurrent中的原子类来保证线程的安全。
下面这段代码就展示了一个很好的volatile的使用场景:
volatile Boolean shutdownRequset;
public void shutdown() {
shutdownRequset = true;
} public void doWork() {
while(!shutdownRequest){
...
}
}
当shutdown()方法被调用的时候,能保证所用的doWork()方法都停下来。
——————————————————————————————————————————————————————————————————————
接下来是第二个方面的用途,普通变量仅仅会保证在该方法执行过程中所有依赖赋值结果的地方都能获得正确的结果,而不能保证变量赋值的操作顺序与程序代码的执行顺序一致。
如果定义的变量没有被volatile修饰,那么就有可能由于指令重排的优化而导致执行顺序的颠倒。
如下面这段代码:
public class Singleton { private volatile static Singleton instance; public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
} public static void main(String[] args) {
Singleton.getInstance();
}
}
通过JIT编译之后就会多执行一个“lock”操作,这个操作相当于一个内存屏障,指重排序之后,不能讲屏障之后的操作放到屏障之前。
不过说到底并不能说volatile有什么执行的迅速的特点,但其开销是比锁低的,,唯一需要看的是volatile是否满足使用场景。
volatile关键字是如何起作用的?的更多相关文章
- 实习第一个月总结(const关键字、条件编译、volatile关键字、#和##的作用、函数指针)
C语言中const关键字的作用: 修饰局部变量或者全局变量,表示变量n的值不能被改变了 修饰指针,分为常量指针与指针常量,也可以两者结合 常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变, ...
- Volatile关键字的两个作用
1.保证修饰的变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个值,新值对于其他线程来说是可以立即得知的. 2.禁止指令重新排序化
- [其他]volatile 关键字
用 volatile 关键字修饰函数 的作用是 告诉编译器该函数不会返回 , 让编译器能产生更好的代码 另外也能避免一些假警告信息,如未初始化的变量等
- [面试必备]深入理解Java的volatile关键字
前言 在Java并发编程中,volatile关键字有着至关重要的作用,在面试中也常常会是必备的一个问题.本文将会介绍volatile关键字的作用以及其实现原理. volatile作用 volatile ...
- Volatile关键字回顾之线程可见性
java中,volatile关键字有两大作用: 1.保证线程的可见性 2.防止指令重排序 这篇文章主要通过典型案例,体现可见性这一特性. 概念: java中,堆内存是线程共享的.而每个线程,都应该有自 ...
- Java并发编程——为什么要用volatile关键字
首发地址 https://blog.leapmie.com/archives/66ba646f/ 日常编程中出现 volatile 关键字的频率并不高,大家可能对 volatile 关键字比较陌生,再 ...
- 深入解析volatile关键字
前言 很高兴遇见你~ 欢迎阅读我的文章. volatile关键字在Java多线程编程编程中起的作用是很大的,合理使用可以减少很多的线程安全问题.但其实可以发现使用这个关键字的开发者其实很少,包括我自己 ...
- 【面试普通人VS高手系列】volatile关键字有什么用?它的实现原理是什么?
一个工作了6年的Java程序员,在阿里二面,被问到"volatile"关键字. 然后,就没有然后了- 同样,另外一个去美团面试的工作4年的小伙伴,也被"volatile关 ...
- Java并发编程学习笔记 深入理解volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...
随机推荐
- Codeforces 817F MEX Queries
题意:对一个维护三种操作:1.将[l..r]中的数全部加入集合中.2.将集合中[l..r]范围内的数删去.3.将集合中在[l..r]中的数删去,并将之前不在集合中的数加入集合 考虑到最近线段树总是写爆 ...
- java 后台代码调用接口
import com.jiuqu.jollykeys.common.util.JsonUtil;import java.io.UnsupportedEncodingException;import j ...
- drupal 开发简单站点流程
友情推广:Uminicmf 一个基于thinkphp开发的OA框架.http://blog.csdn.net/youmypig/article/details/51727713 drupal 简单站点 ...
- 在线安装eclipse中html/jsp/xml editor插件(很可靠)
之前有一篇文章也是安装eclipse中的web开发插件 ,可是经过非常多人使用,那种方法.不是全部人使用都能够. 接下来,找到一种很管用的方法.就是在线安装. 废话不多说,这样的方法绝对能够成功安装. ...
- UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 46:illegal multibyte sequence
一. 最近使用python写入文件时,出现了如下的错误: 但是content的内容是unicode编码,不知道怎么和gbk扯上了关系,对content使用encode()和decode(),用gbk, ...
- linux应用态下的时间
1.时间值 1.1 日历时间(UTC) 该值是自1 9 7 0年1月1日0 0 : 0 0 : 0 0以来国际标准时间( U T C)所经过的秒数累计值(早期的手册称 U T C为格林尼治标准时间) ...
- org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
异常信息 org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable represen ...
- JavaScript实现段落文本高亮
代码: <!doctype html> <html lang="en"> <head> <meta http-equiv="Co ...
- mongodb设置用户名和密码
需求:我们需要在一个mongodb上面新建两个数据库,每个数据库的用户名和密码不一样,讲道理来说我们直接设置admin,就可以控制所有的数据库,不过用起来总是感觉有各种问题,目前还不太熟悉mongod ...
- JeeSite如何正确连接SQL SERVER 数据库
JeeSite如何正确连接SQL SERVER 数据库 jeesite介绍 感谢jeesite项目的作者thinkgem. 没有你我也不会更改这数据源非了恁大的劲,,,,嘻嘻嘻说多了. JeeSite ...