volatile(内存可见性问题)

本文通过一个实例演示了在多线程环境下,不使用volatile关键字时内存不可见的问题,并对比展示了使用volatile后的变化。深入探讨了volatile关键字如何确保数据的可见性,以及其在轻量级同步策略中的应用。

模拟一个内存不可见导致的问题

volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
相较于 synchronized 是一种较为轻量级的同步策略。
1. volatile 不具备“互斥性”
2. volatile 不能保证变量的“原子性”


package com.fjf.juc;

/**
* 2018年7月20日 19:36:23
* @author fjf
*
* volatile 关键字
*
* 模拟一个因为内存不可见导致的问题
*
*/
public class TestVolatile {

public static void main(String[] args) {

    ThreadDemo td = new ThreadDemo();
    new Thread(td).start();

    //ThreadDemo2 td2 = new ThreadDemo2();
    //td2.start();


    while(true){           //主要原因是while(true)的执行效率高,偏底层。main都来及去主线程中去同步
    /*  try {
            //Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
    //if(true){
        //System.out.println(Thread.currentThread().getName());
        if(td.isFlag()){
            System.out.println("----------*****-------");
            break;
        }
    }


}

}

//线程类 实现runable接口
class ThreadDemo implements Runnable{

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

    @Override
    public void run() {

        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = true;

        System.out.println("flag="+flag);


    }


    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

}


//继承Thread类 来创建多线程
class ThreadDemo2 extends Thread{

    private boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = true;

        System.out.println("flag="+flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

}

没有volatile时,线程thread-0 把flag变成true,但是main线程中if(td.isflag())一直还是false,导致“——*—–”输不出来,程序无法结束。*
这里写图片描述

加上volatile后,thread-0修改flag后,main可以获取到,程序输出,结束。
这里写图片描述


设计的思路:
1.共享变量flag;
2.thread-0线程去写(修改),main线程去读。而且先thread-0 sleep会,让main读走旧值
3.while(true)的效率特别高,main线程都无法同步主存。

<think>嗯,用户的问题是关于Java中volatile关键字如何保证内存可见性的,还希望有交互图来解释。我需要先理解内存可见性的基本概念,然后结合volatile的工作原理来回答。首先,用户可能已经知道JMM(Java内存模型)的基础,但需要更深入的机制解释。 用户提到了引用资料1到4,这些资料里提到JMM、volatile的作用、内存屏障和CPU执行流程。我需要整合这些信息。比如,引用4提到volatile通过某种机制确保修改后的值立即写入主内存,并让其他线程的缓存失效。这可能涉及到内存屏障和缓存一致性协议,比如MESI。 接下来,用户想要交互图示例。我需要考虑如何用图示来展示没有volatile时的可见性问题,以及使用volatile后如何解决。可能需要画两个场景:普通变量和volatile变量的对比,展示线程工作内存和主内存的交互。 然后,思考volatile的实现机制。引用3提到OrderAccess::storeload(),这应该是一个内存屏障,防止指令重排序。同时,引用2指出volatile遵循happens-before原则,禁止重排序影响多线程结果。需要解释编译器和CPU层面的重排序,以及内存屏障如何阻止它们。 用户可能存在的疑问点包括:为什么volatile不能保证原子性?可见性和原子性的区别是什么?这时候需要区分这两个概念,并指出volatile仅解决可见性,而原子性需要其他机制如synchronized。 最后,组织回答结构,先介绍可见性问题,再解释volatile的底层原理,结合交互图说明,最后总结注意事项。确保使用用户要求的格式,比如行内公式用$...$,正确的中文回答,并生成相关问题。</think>### Java volatile内存可见性原理及交互图解析 #### 一、内存可见性问题背景 在Java多线程环境下,普通变量修改的可见性可能失效。例如: - 线程A修改变量后,新值可能仅停留在**工作内存**(CPU缓存) - 线程B读取时仍从**主内存**获取旧值 $$ \begin{array}{ccc} \text{主内存} & \xrightarrow{\text{读取旧值}} & \text{线程B工作内存} \\ & \nwarrow & \downarrow \\ \text{线程A工作内存} & \xleftarrow{\text{写入新值}} & \\ \end{array} $$ #### 二、volatile实现原理 通过**JMM规范+硬件层支持**实现可见性保证: 1. **编译器层面** - 禁止对volatile变量的指令重排序 - 插入内存屏障(Memory Barrier) ```java // 写操作后插入StoreLoad屏障 OrderAccess::storeload(); ``` 2. **CPU层面** - 基于**缓存一致性协议**(如MESI) - 强制将工作内存修改立即刷新到主内存 - 使其他CPU核心的对应缓存行失效 #### 三、交互流程示意图 ```mermaid sequenceDiagram participant 主内存 participant 线程A participant 线程B 线程A->>主内存: 1. volatile写操作 Note over 线程A,主内存: 强制刷新到主内存内存-->>线程B: 2. 使缓存行失效 线程B->>主内存: 3. 重新读取最新值 ``` #### 四、实现机制详解 | 阶段 | 普通变量 | volatile变量 | |----------|----------------------|---------------------------| | 读取操作 | 可能从工作内存读取旧值 | 必须从主内存读取最新值 | | 写入操作 | 可能延迟写入主内存 | 立即刷新到主内存 | | 指令重排序 | 允许 | 禁止(通过内存屏障) | | 缓存一致性 | 依赖CPU协议被动更新 | 主动触发缓存失效通知 | #### 五、注意事项 1. **不保证原子性** - 例如`volatile int i=0; i++`仍需`synchronized`保证原子性 2. **适用场景** - 状态标志位(如`isRunning`) - 单次写入的多线程共享变量 3. **性能影响** - 频繁写操作时会影响性能(需频繁刷新内存) [^1]: JMM规范定义的内存访问规则 [^2]: volatile的happens-before特性 [^3]: 内存屏障的具体实现 [^4]: CPU缓存一致性协议支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值