在当前的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. bzoj 4930: 棋盘

    Description 给定一个n×n的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置(x,y),(u,v)能互相攻击当前仅 当满足以下两个条件: 1:x=u或y=v 2:对于(x,y)与 ...

  2. ThinkJava-复用类

    7 .2 继承语法 例子: package com.cy.reusing; import static com.java.util.Print.*; class Cleanser { private ...

  3. ubuntu 14.04 lamp 安装与配置

    一.安装apache 1.打开终端:Ctrl+Alt+T sudo apt-get update 2.通过apt-get方式安装Apache: sudo apt-get install apache2 ...

  4. oletools下载安装及rtfobj使用

    rtf内嵌对象分析提取工具rtfobj是oletools的一部分 oletools各个版本下载地址https://bitbucket.org/decalage/oletools/downloads/ ...

  5. golang web框架 beego 学习 (四) 连接mysql

    1 DB参数配置在app.conf appname = gowebProject httpport = runmode = dev [db] host= localhost port= databas ...

  6. jQuery的get()用法

    这个方法主要是将jQuery对象或者jQuery对象集合转换成DOM对象或dom对象集合. get()方法中如果传递参数,表示将具体位置的jQuery对象转换成dom对象.如果没有参数,则表示返回所有 ...

  7. Fork-Join分治编程介绍(一)

    一.Fork-Join 框架介绍 1. 什么是 Fork-Join 分治编程框架   Fork/Join框架是Java7提供了的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每 ...

  8. JavaScript词法分析(尽力理解)

    JavaScript中在调用函数的那一瞬间之前,会先进行词法分析 词法分析的过程: 当函数调用的前一瞬间,会先形成一个激活对象:Avtive Object(AO),并会分析以下3个方面: 1:函数参数 ...

  9. Wechall 部分WP

    前言: 开始打CTF,掌握一些新的姿势与知识. 这里我选择的平台是Wechall.这里从简单到难 WP部分: Training: Get SourcedAnswer: 查看网页源代码 Training ...

  10. openStack虚拟机error 错误状态基于差异镜像+基镜像做恢复