在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

用volatile和不用volatile的区别,运行一下,就知道了。

不用volatile:

  1. package com.keyword;
  2. public class TestWithoutVolatile {
  3. private static boolean bChanged;
  4. public static void main(String[] args) throws InterruptedException {
  5. new Thread() {
  6. @Override
  7. public void run() {
  8. for (;;) {
  9. if (bChanged == !bChanged) {
  10. System.out.println("!=");
  11. System.exit(0);
  12. }
  13. }
  14. }
  15. }.start();
  16. Thread.sleep(1);
  17. new Thread() {
  18. @Override
  19. public void run() {
  20. for (;;) {
  21. bChanged = !bChanged;
  22. }
  23. }
  24. }.start();
  25. }
  26. }

运行后,程序进入死循环了,一直在运行。

用volatile:

  1. package com.keyword;
  2. public class TestWithVolatile {
  3. private static volatile boolean bChanged;
  4. public static void main(String[] args) throws InterruptedException {
  5. new Thread() {
  6. @Override
  7. public void run() {
  8. for (;;) {
  9. if (bChanged == !bChanged) {
  10. System.out.println("!=");
  11. System.exit(0);
  12. }
  13. }
  14. }
  15. }.start();
  16. Thread.sleep(1);
  17. new Thread() {
  18. @Override
  19. public void run() {
  20. for (;;) {
  21. bChanged = !bChanged;
  22. }
  23. }
  24. }.start();
  25. }
  26. }

程序输出!=,然后马上退出。

但是,很多情况下,用不用volatile,感觉不出什么区别,什么时候要用volatile呢?看看JDK里使用volatile的类。

比如java.util.regex.Pattern里的变量:

  1. private transient volatile boolean compiled = false;

还有,java.lang.System的变量:

  1. private static volatile Console cons = null;

一般就是初始化的时候,需要用到volatile。

java.util.Scanner里的变量,如:

  1. private static volatile Pattern boolPattern;
  2. private static volatile Pattern separatorPattern;
  3. private static volatile Pattern linePattern;

初始化boolPattern的代码:

  1. private static Pattern boolPattern() {
  2. Pattern bp = boolPattern;
  3. if (bp == null)
  4. boolPattern = bp = Pattern.compile(BOOLEAN_PATTERN,
  5. Pattern.CASE_INSENSITIVE);
  6. return bp;
  7. }

上面的情况,可以使用synchronized来对boolPattern加锁,但是synchronized开销比volatile大,volatile能够胜任上面的工作。

volatile不保证原子操作,所以,很容易读到脏数据。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

java 用volatile和不用volatile的区别的更多相关文章

  1. java 里面保留字volatile及其与synchronized的区别

           锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility).互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议, ...

  2. 深入理解 Java 内存模型 JMM 与 volatile

    Java 内存模型(Java Memory Model,简称 JMM)是一种抽象的概念,并不真实存在,它描述的是一组规范或者规则,通过这种规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象 ...

  3. 多线程的指令重排问题:as-if-serial语义,happens-before语义;volatile关键字,volatile和synchronized的区别

    一.指令重排问题 你写的代码有可能,根本没有按照你期望的顺序执行,因为编译器和 CPU 会尝试指令重排来让代码运行更高效,这就是指令重排. 1.1 虚拟机层面 我们都知道CPU执行指令的时候,访问内存 ...

  4. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

  5. 全面理解Java内存模型(JMM)及volatile关键字

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...

  6. 【Java并发编程】:volatile变量修饰符

    volatile用处说明     在JDK1.2之前,java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的.而随着JVM的成熟和优化,现在在多线程环境下volatile关键 ...

  7. Java并发编程之三:volatile关键字解析 转载

    目录: <Java并发编程之三:volatile关键字解析 转载> <Synchronized之一:基本使用>   volatile这个关键字可能很多朋友都听说过,或许也都用过 ...

  8. 全面理解Java内存模型(JMM)及volatile关键字(转)

    原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...

  9. Java并发之(1):volatile关键字(TIJ21-21.3.3 21.3.4)

    Java并发Java服务器端编程的一项必备技能. ** 1 简介    volatile是java中的一个保留关键字,它在英语中的含义是易变的,不稳定的.volatile像final.static等其 ...

随机推荐

  1. PHP中imagecopyresampled参数详解

    原文链接http://blog.csdn.net/ajaxchen_615/article/details/5941181 做php缩微图程序,用到了imagecopyresampled函数,在网上找 ...

  2. typescript-dva脚手架

    2019有太多的东西想尝试,ts,GraphQL,SSR,docker,python,electron,小程序云后台,vue3等等,一个个来吧,用两天了解了下typescript,大概做了个webpa ...

  3. NoSQL非结构化数据库高级培训课程-大纲

    一.课程概述 本课程面向No-SQL开发人员.系统分析和系统架构师,目的在于帮助他们建立起完整的No-SQL数据库的概念,应用场景.相关开源技术框架和优缺点. 二.课程大纲 主题 时间 主题 No-S ...

  4. Asio 注意事项

    本文列举 Asio 各种值得注意的细节. 另见:基于 Asio 的 C++ 网络编程 No Deprecated 在包含 Asio 头文件之前,定义宏 BOOST_ASIO_NO_DEPRECATED ...

  5. 导入wordpress数据库到mysql报错

    mysql字符集编码错误的导入数据会提示错误了,这个和插入数据一样如果保存的数据与mysql编码不一样那么肯定会出现导入乱码或插入数据丢失的问题,下面我们一起来看一个例子. 恢复数据库报错:由于字符集 ...

  6. Bezier画线算法

    编译器:VS2013 描述:Bezier画线是利用导数相同拼接曲线,使曲线十分光滑,而不是随意拼接观赏性很差 主函数段 #include "stdafx.h" #include&l ...

  7. IE下的Firebug——IE WebDeveloper js debug

    原文地址:http://blog.csdn.net/wangbin1986/article/details/6837285 对于大部分做前端设计者而言应该都使用过Firefox浏览器下一款调试网站的扩 ...

  8. ubuntu14.04 login loop issue

    无法进入图形界面的所有问题几乎都碰到了,可惜尝试所有办法,还是各种broken packages 等,无法重装 ubuntu-desktop 成功. 耽误了2天,果断决定重装系统算了..尽管很多软件, ...

  9. Web 使用反射获得一个对象的所有get方法

    问题描述: 由于想知道request中包含哪些getter方法,就想通过反射进行遍历,然后输出,结果异常,异常信息: 问题代码: try { outGetter(request); } catch ( ...

  10. rman备份恢复命令之switch(转)

    一 switch 命令1 switch命令用途更新数据文件名为rman下镜像拷贝时指定的数据文件名更新数据文件名为 set newname 命令指定的名字. 2 switch 命令使用前提条件rman ...