关键字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关键字是如何起作用的?的更多相关文章

  1. 实习第一个月总结(const关键字、条件编译、volatile关键字、#和##的作用、函数指针)

    C语言中const关键字的作用: 修饰局部变量或者全局变量,表示变量n的值不能被改变了 修饰指针,分为常量指针与指针常量,也可以两者结合 常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变, ...

  2. Volatile关键字的两个作用

    1.保证修饰的变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个值,新值对于其他线程来说是可以立即得知的. 2.禁止指令重新排序化

  3. [其他]volatile 关键字

    用  volatile 关键字修饰函数 的作用是 告诉编译器该函数不会返回 , 让编译器能产生更好的代码 另外也能避免一些假警告信息,如未初始化的变量等

  4. [面试必备]深入理解Java的volatile关键字

    前言 在Java并发编程中,volatile关键字有着至关重要的作用,在面试中也常常会是必备的一个问题.本文将会介绍volatile关键字的作用以及其实现原理. volatile作用 volatile ...

  5. Volatile关键字回顾之线程可见性

    java中,volatile关键字有两大作用: 1.保证线程的可见性 2.防止指令重排序 这篇文章主要通过典型案例,体现可见性这一特性. 概念: java中,堆内存是线程共享的.而每个线程,都应该有自 ...

  6. Java并发编程——为什么要用volatile关键字

    首发地址 https://blog.leapmie.com/archives/66ba646f/ 日常编程中出现 volatile 关键字的频率并不高,大家可能对 volatile 关键字比较陌生,再 ...

  7. 深入解析volatile关键字

    前言 很高兴遇见你~ 欢迎阅读我的文章. volatile关键字在Java多线程编程编程中起的作用是很大的,合理使用可以减少很多的线程安全问题.但其实可以发现使用这个关键字的开发者其实很少,包括我自己 ...

  8. 【面试普通人VS高手系列】volatile关键字有什么用?它的实现原理是什么?

    一个工作了6年的Java程序员,在阿里二面,被问到"volatile"关键字. 然后,就没有然后了- 同样,另外一个去美团面试的工作4年的小伙伴,也被"volatile关 ...

  9. Java并发编程学习笔记 深入理解volatile关键字的作用

    引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...

随机推荐

  1. java把html标签字符转普通字符(反转换成html标签)(摘抄)

    下面是java把html标签字符转换,我用了spring 包中的 org.springframework.web.util.HtmlUtils 了解了源代码并且进步了使用,发现写得真不错...同时也可 ...

  2. Sagit.Framework For IOS 开发框架入门开发教程1:框架下载与环境配置

    背景: 前天开源了框架:开源:Sagit.Framework For IOS 开发框架 所以注定要追补一套开发教程了,所以尽量抽空了!!! 步骤 1:下载框架源码 GitHub:https://git ...

  3. Android测试:Fundamentals of Testing

    原文地址:https://developer.android.com/training/testing/fundamentals.html 用户在不同的级别上与你的应用产生交互.从按下按钮到将信息下载 ...

  4. Codeforces Round #313 (Div. 2) C

    题目链接 题意:  有一个六边形,给你6条边的长度(顺时针给出).每条边都是整数,问你它能够被切割成几个单位长度的正三角形  (题目保证给出的数据能够被切割) 思路: 六边形能够被切割成两种情况: ① ...

  5. Android事件拦截机制简单分析

    前一阶段,在学习的时候,遇到了我觉得的我接触安卓以来的最多的一次事件拦截出来,那个项目,用到了slidemenu側滑菜单条,然后加上tab标签,还有轮播广告,listview上下滑动.viewpage ...

  6. Flex/AS3 base64指定字符编码

    public static function base64Encode(str:String, charset:String = "GBK"):String{ if(StringU ...

  7. sqlserver 存储过程 分页搜索查询

    具体调用实例见代码最后一行注释区域 --if exists(select * from sysobjects where id = object_id(N'page_search') and type ...

  8. iOS OC应用异常捕获,崩溃退出前返回信息给后台

    第三方的了,有友盟,腾讯的bugly 查了一下网上类似的代码很多,在借鉴前辈的代码后,组合了一下: 1.捕获异常信息 2.获得当前日期,版本,系统 3.获得出bug的视图控制器转为字符串 4.将前3条 ...

  9. sftp新建用户步骤

    1,切换root用户2,创建sftp组:groupadd sftp3,创建用户并限制进入路径:useradd -g sftp -s /bin/false testuser 修改密码: passwd t ...

  10. 创建一个可用的简单的SpringMVC项目,图文并茂

    转载麻烦注明下来源:http://www.cnblogs.com/silentdoer/articles/7134332.html,谢谢. 最近在自学SpringMVC,百度了很多资料都是比较老的,而 ...