当前的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详解的更多相关文章

  1. 【转】 java中HashMap详解

    原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...

  2. java中HashMap详解(转)

    java中HashMap详解 博客分类: JavaSE Java算法JDK编程生活       HashMap 和 HashSet 是 Java Collection Framework 的两个重要成 ...

  3. java集合(2)- java中HashMap详解

    java中HashMap详解 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 H ...

  4. java中多线程详解-synchronized

    一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...

  5. JAVA 中 synchronized 详解

    看到一篇关于JAVA中synchronized的用法的详解,觉得不错遂转载之..... 原文地址: http://www.cnblogs.com/GnagWang/archive/2011/02/27 ...

  6. Java 中HashMap 详解

    本篇重点: 1.HashMap的存储结构 2.HashMap的put和get操作过程 3.HashMap的扩容 4.关于transient关键字 HashMap的存储结构 1. HashMap 总体是 ...

  7. Java中List详解

    List是Java中比较常用的集合类,关于List接口有很多实现类,本文就来简单介绍下其中几个重点的实现ArrayList.LinkedList和Vector之间的关系和区别. List List 是 ...

  8. Java中PriorityQueue详解

    Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度, ...

  9. 【Java基础】JAVA中优先队列详解

    总体介绍 优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素).这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序( ...

随机推荐

  1. hive之案例分析(grouping sets,lateral view explode, concat_ws)

    有这样一组搜索结果数据: 租户,平台, 登录用户, 搜索关键词, 搜索的商品结果List {"tenantcode":"", "platform&qu ...

  2. hive常用参数配置设置

    hive.exec.mode.local.auto 决定 Hive 是否应该自动地根据输入文件大小,在本地运行(在GateWay运行) true hive.exec.mode.local.auto.i ...

  3. Codeforces Round #256 (Div. 2) B (448B) Suffix Structures

    题意就是将第一个字符串转化为第二个字符串,支持两个操作.一个是删除,一个是更换字符位置. 简单的字符串操作!. AC代码例如以下: #include<iostream> #include& ...

  4. openvpn之安装篇

    之前整过openvpn,现把一些过程记录下来,方便以后查阅. #!/bin/bash cd /usr/local/srcyum install pam-devel.x86_64 -ywget http ...

  5. 使用postman测试文件上传

    调试API神奇----postman 请求方法:POST Body-->form-data-->key(选择file) QQ技术交流群:282575808 ---------------- ...

  6. android 编译错误 Error:(1, 0) Plugin with id 'com.android.application' not found.

    在导入一个项目时,由于它本身的gradle版本比较高,你试用比较旧版本的gradle时就报出Plugin with id 'com.android.application' not found.的错误 ...

  7. R语言-分组统计

    分组统计 1.假定有一组成绩数据,要求根据性别进行分组统计: > score    ID   score1 score2 Gender1  101 11.35321    0.9   male2 ...

  8. Sword redis配置

    Redis 配置 Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf. --查看配置 你可以通过 CONFIG 命令查看或设置配置项(使用CONFIG必须在redis客 ...

  9. 树莓派 NOOBS 安装系统

    Raspberry Pi Foundation发布了一个名为“New Out of Box Software”(NOOBS)的工具,可以用来方便的安装系统,让我们尝试一下这个新的安装工具. 以我的树莓 ...

  10. Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat 解决办法

    问题描述 安装 Python的MySQL驱动时时出现这个错误: Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat 环境 ...