Java中Volatile详解

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

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

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

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

不用volatile:

[java] view plain copy

  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:

[java] view plain copy

  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里的变量:

[java] view plain copy

  1. private transient volatile boolean compiled = false;

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

[java] view plain copy

  1. private static volatile Console cons = null;

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

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

[java] view plain copy

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

初始化boolPattern的代码:

[java] view plain copy

  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代码块中,或者为常量时,不必使用

时间: 2024-10-25 15:34:15

Java中Volatile详解的相关文章

【转】 java中HashMap详解

原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 H

java中运算符详解

前言 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.JAVA中常见的运算符有很多种,大致分为以下几种,常见的几种运算符如下图: 算术运算符 加.减.乘.除.求余.例++.--.%./.  赋值运算符 为变量或常量起到赋值作用的.例如=.+=.*= 关系运算符 判断数据大小的,结果为一个布尔值.例如>.>=.!=.== 逻辑运算符 进行逻辑运算,运算的成员为布尔值,结果也为布尔值.例如&&.||.!. 条件运算符 也称三目运算符,表达式为(a<b)?a:b

JAVA中priorityqueue详解

Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度,将让读者建立对PriorityQueue建立清晰而深入的认识. 总体介绍 前面以Java?ArrayDeque_为例讲解了_Stack_和_Queue,其实还有一种特殊的队列叫做_PriorityQueue_,即优先队列.优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每

java中HashSet详解

HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码: Java代码   public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { // 使用 HashM

java中HashMap详解

原文:http://alex09.iteye.com/blog/539545 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 通过 HashMap.HashSet 的源代码分析

Java中MVC详解以及优缺点总结

 概念:  MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务数据.逻辑.界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑.MVC被独特的发展起来用于映射传统的输入.处理和输出功能在一个逻辑的图形化用户界面的结构中. 最典型的MVC就是Jsp + Servlet + JavaBean的模式 MVC开始是存在于桌面程

java中HashMap详解(转)

HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 通过 HashMap.HashSet 的源代码分析其 Hash 存储机制 实际上,HashSet 和 HashMap 之间有很多

java中HashSet详解(转)

HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码: Java代码   public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { // 使用 HashM

java中static详解

static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何对象.也就是说,它不依赖类特定的实例,被类的所有实例共享. 只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们.因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象. 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类