Java虚拟机内存模型和volatile型变量

Java虚拟机内存模型

了解Java虚拟机的内存模型,有助于我们明白为什么会发生线程安全问题.

上面这幅图是<深入理解Java虚拟机-JVM高级特性与最佳实践>的书中截图.

线程共享的变量会保存在主内存中(Main Memory).

而线程共享的变量的副本会保存在每个线程各自的工作内存中(Working Memory).

线程对于共享变量的所有操作(读取,赋值等)都必须在工作内存中进行,不能直接读写主内存的变量.

不同的线程之间,也无法访问其他线程的工作内存.线程之间的变量传递需要通过主内存来完成.

所以发生线程安全问题的根源就在于,当共享变量在主内存中被修改后,有的线程的工作内存保存的还是该被修改的共享变量的旧的值,就导致了数据不一致的后果.

Volatile型变量

被Volatile修饰的变量,是轻量化的解决部分线程安全问题的方法,它具有2种特性:

1. 保证此变量对于所有线程的可见性,指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的.

但是这里所说的立即得知,也并不能保证Volatile就能完全避免线程安全数据一致的问题.

private volatile int race = 0;

private void increase() {
    race++;
}

比如上面代码,如果多线程的调用increase()方法,是并不能保证线程安全,而且是很有可能发生数据不一致的问题.

原因在于,虽然volatile保证了race变量的修改被其他线程立即得知,但是本身race++这行代码并不是原子操作.实际代码编译后会变成多条字节码.

volatile的使用场景需要符合2条规则:

1) 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值;

2) 变量不需要与其他的状态变量共同参与不变约束.

volatile型变量的经典使用场景是如下这种.当doSomthing()方法被多线程调用时候,只需把shutDown=true之后,所有线程就不会在进行下一轮的while.

private volatile boolean shutDown = false;

private void doSomething() {
    while (!shutDown) {
        // do something
    }
}

2. 使用Volatile变量能禁止指令重排序.

普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致.因为在一个县城的方法执行过程中无法感知到这点.

public VolatileExample {
    Map configOptions;
    char[] configText;
    volatile boolean initiallized = false;

    private void init() {
        configOptions = new HashMap();
        configText = readonfigFile(fileName);
        processConfigOptions(configText, configOptions);
        initialized = true;
    }

    private void do() {
        while (!initialized) {
            sleep();
        }
        doSomethingWithconfig();
    }
}

上面这段代码,假设init()方法和do()方法运行再不同的线程中.如果initialized变量没有使用volatile修饰的话,init()方法可能由于指令重排序的优化(指令重排序是机器级的优化操作,提前执行时指这句话对应的汇编代码会被提前执行),导致最后一句代码initialized=true被提前执行.这种情况的后果就是do()方法因为initialized=true就跳过了while循环去执行doSomethingWithConfig()了,但其实可能init()的方法还未运行结束.

而volatile关键字可以避免此类情况的发生.

时间: 2024-11-05 16:05:10

Java虚拟机内存模型和volatile型变量的相关文章

Java线程角度的内存模型和volatile型变量

内存模型的目标是定义程序中各个变量的访问 规则,即在虚拟机中将变量(包括实例字段,静态字段和构成数组对象的元素,不包括局部变量与方法参数,因为后者是线程私有的)存储到内存和从内存中取出变量这样的底层细节. Java内存模型规定所有的变量都存储在住内存,每条线程还有自己的工作内存,工作内存保存了被该线程使用到的变量和主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存的变量,不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成. 关键

Java同步内存模型和Volatile关键字

计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度.因此在CPU里面就有了高速缓存. 也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和

黑马-----内存模型和volatile详解

黑马程序员:Java培训.Android培训.iOS培训..Net培训 JAVA线程-内存模型和volatile详解 一.单核内存模型 1.程序运行时,将临时数据存放到Cache中 2.将CPU计算所需要的数据从Cache中拷贝一份到H Cache中 3.CPU直接从H Cache中读取数据进行计算 4.CPU将计算的结果写入H Cache中 5.H Cache将最新的结果值涮入Cache中(何时写入不确定) 6.将Cache中结果数据写回程序(如果有需要,例如文件.数据库) 需要H Cache

并发编程-(3)Java内存模型和volatile

目录 1.内存模型概念 2.多线程的特性 1.1.原子性 1.2.可见性 1.3.有序性 2.Java内存模型 2.1.JMM和JVM 2.2.Java内存模型(JMM) 2.2.1.案例 2.2.2.volatile作用 2.3.重排序 2.3.1.什么是重排序 2.3.2.重排序如何影响线程安全 2.4.总结 1.内存模型概念 我们都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的

Java内存模型和JVM内存管理

Java内存模型和JVM内存管理   一.Java内存模型: 1.主内存和工作内存(即是本地内存): Java内存模型的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节.此处的变量与Java编程里面的变量有所不同步,它包含了实例字段.静态字段和构成数组对象的元素,但不包含局部变量和方法参数,因为后者是线程私有的,不会共享,当然不存在数据竞争问题(如果局部变量是一个reference引用类型,它引用的对象在Java堆中可被各个线程共享,但是ref

JVM内存结构、Java内存模型和Java对象模型

Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚.比如本文要讨论的JVM内存结构.Java内存模型和Java对象模型,这就是三个截然不同的概念,但是很多人容易弄混. 首先,这三个概念是完全不同的三个概念.本文主要目的是对这三个概念加以区分以及做简单的介绍.而这每一个知识点都是又都是比较复杂的.以后会单独写文章做详细介绍. Jvm内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过

【转】java虚拟机内存原型

本文主要通过分析Java内存分配的栈.堆以以及常量池详细的讲解了其的工作原理. 一.java虚拟机内存原型 寄存器:我们在程序中无法控制栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中堆:存放用new产生的数据静态域:存放在对象中用static定义的静态成员常量池:存放常量非RAM存储:硬盘等永久存储空间. 二.常量池(constant pool) 常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据.除了包含代码中所定义的各种基本类型(如int.

volatile型变量自增操作的隐患

??用FindBugs跑自己的项目,报出两处An increment to a volatile field isn't atomic.相应报错的代码如下: volatile int num = 0; num++; ??FindBugs针对这种类型的错误给出了相应的解释 An increment to a volatile field isn't atomic This code increments a volatile field. Increments of volatile fields

关于java虚拟机内存管理的一些讲解

java数据类型: 1)原始类型:Primitive Types(原始值) 数值类型(Numeric Types) 整型类型(Integral Types),浮点类型(Floating-Point Types) 布尔类型(Boolean Types) returnAddress类型:表示一条字节码指令的操作码(Opcode).在所有的虚拟机支持的原始类型之中,只有 returnAddress 类型是不能直接 Java 语言的数据类型对应起来的. 2)引用类型:Reference Types(引用