JMM-volatile

JMM-volatile

一:概述

JMM 是Java内存模型( Java Memory Model),简称JMM。它本身只是一个抽象的概念,并不真实存在,它描述的是一种规则或规范,是和多线程相关的一组规范。通过这组规范,定义了程序中对各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。需要每个JVM 的实现都要遵守这样的规范有了JMM规范的保障,并发程序运行在不同的虚拟机上时,得到的程序结果才是安全可靠可信赖的。如果没有JMM 内存模型来规范,就可能会出现,经过不同 JVM 翻译之后,运行的结果不相同也不正确的情况。

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

二:JMM模型图

1:read/load是一组;use/assign是一组;store/write是指一组;lock/unlock是一组;每组是不可分割独立存在的

2:线程内存数据变化后会自动触发store/write,将修改后的变量写入主存;

3:主存数据的变化,线程内存是无法感知到的,可以通过volatile关键字修饰主存变量让线程感知到主存数据变化;

三:volatile三大特性

1:保证可见性

1.1:概念

只要有一个线程对共享变量的值做了修改,其它线程都将马上收到通知,立即获取最新的值(实际是所有线程直接操作主内存变量)。

1.2:现象举例

package com.test.thread;

/**
 * 线程一种现象
 * 线程不结束
 */
public class VolatileTest {

    private static boolean flag = false;
    //private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {

        //t线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(!flag){

                }
            }
        }).start();

        Thread.sleep(1000l);//等待一秒,等待线程启动
        flag = true;
    }
}

1.3:原因分析

初始状态, t线程刚开始从主内存读取了 flag 的值到工作内存。
因为 t 线程要频繁从主内存中读取 flag 的值,JIT 即时编译器在运行一段时间后,会将程序优化,将 flag 的值缓存至自己工作内存中的高速缓存中,减少对主存中 flag 的访问,提高效率
1 秒之后,main 线程修改了 flag 的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值

1.4:解决方案

    我们可以使用 volatile 关键字来修饰 flag,它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存。
    private static volatile boolean flag = true;

2:不保证原子性

2.1:概念

    如上例子中体现的那样,可见性保证的是在多个线程之间,一个线程对 volatile 变量的修改对另一个线程可见,但是,volatile 修饰的变量并不能保证原子性,因为它只能保证当前线程看到的值为最新值,并不能解决指令的交错问题,比如两个线程分别做 i++ 和 i--,操作,就会出现指令交错的问题;volatile不能够解决该类问题。
    为了保证原子性,还是需要乖乖的使用 synchronized 或者 ReentrantLock
    或者是有JUC提供的原子变量,Atomic相关类。

3:保证有序性

3.1:概念

JVM 会在不影响正确性的前提下,可以调整语句的执行顺序;如下一块代码:

static int i;
static int j;

// 在某个线程内执行如下赋值操作
i = ...; 
j = ...; 

无论是先给i赋值,还是先给j赋值,都不会影响程序的正序性。这种特性称之为『指令重排』,但是多线程下『指令重排』会影响正确性。

volatile关键字可以保证被修饰的变量不会被进行指令重排。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值