volatile~多线程下变量不可见

原文链接:volatile~多线程下变量不可见 – 编程屋

目录

1 概述

2 案例

3 JMM内存模型

4 如何解决

4.1 加锁

4.2 volatile关键字


1 概述

        在多线程并发执行下,多个线程修改共享的成员变量,会出现一个线程修改了共享变量的值后,另一个线程不能直接看到该线程修改之后的最新值。

2 案例

代码示例:

public class VisibilityDemo01 {

    public static void main(String[] args) {

        //1. 开启一个子线程
        MyThread myThread = new MyThread();
        myThread.start();

        //2. 主线程执行
        while (true) {
            if (myThread.isFlag()){
                System.out.println("主线程进入循环执行");
            }
        }

    }
}

class MyThread extends Thread{
    //成员变量
    private  boolean flag = false;

    @Override
    public void run() {
        //模拟业务代码耗时
        try {
            Thread.sleep(1000);
        } 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;
    }
}

运行结果:

可见,当子线程修改了变量的值时,主线程依然没有读取到修改之后的值。

3 JMM内存模型

JMM(Java Memory Model):java内存模型,是java虚拟机规范中所定义的一种内存模型,java内存模型是标准化的,屏蔽掉了底层不同计算机的区别。

java内存模型描述了java程序中各种变量(线程共享)的访问规则,以及在jvm中将变量存储到内存和从内存中读取变量这样的底层细节。

JMM有以下规定:

  • 所有的共享变量都存储于主内存。这里的变量指的是实例变量和类变量。不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。
  • 每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本
  • 线程对变量的所有操作(读、取)都必须在工作内存中完成,而不能直接读写主内存中的变量
  • 不同线程之间也不能直接访问对方工作内存中的变量,线程之间变量的值的传递需要通过主内存中转来完成。

本地内存和主内存的关系:

问题分析:

因为成员变量是定义在类中,所以它的变量值存储在主内存中,

1)子线程从主内存读取到数据放入其对应的工作内存

2)将flag的值修改为true ,但是这个时候还没有写会到主内存

3)此时main方法读取到了flag的值为false(这就是上面模拟业务代码休眠一秒的原因,不然子线程速度太快值刚修改完就写回了主内存,那么main线程读取到的值就是最新的值了)

4)当子线程t将flag的值写回去后,但是main函数里的while(true)调用的是系统比较底层的代码,速度快,快到没有时间去读取主内存中的值。

所以while(true)读物到的值一直是false。(如果此时有一个时刻main线程从主内存读取到了主内存flag的最新值,那么if语句就可以执行,main线程何时从主内存中读取最新的值,我们无法控制)

可见性问题原因:所有的共享变量存于主内存中,每个线程有自己的本地内存,而且线程读写共享数据也是通过本地内存交换的,所以导致了可见性问题。

4 如何解决

那么既然已经知道了产生可见性问题的原因,那么如何解决它呢?

4.1 加锁

将上述主线程里面的代码改成如下:

        //2. 主线程执行
        while (true) {
            synchronized (myThread){
                if (myThread.isFlag()){
                    System.out.println("主线程进入循环执行");
                }
            }

        }

运行结果:

发现可以读取到子线程修改之后的值。

原因:

1)线程获得锁

2)清空工作内存

3)将主内存拷贝共享变量最新值到工作内存中成为副本

4)执行代码

5)将修改后的副本的值刷新回主内存中

6)线程释放锁

4.2 volatile关键字

将子线程中的部分代码修改如下:

    //成员变量
    private volatile boolean flag = false;

 1)子线程t从主内存读取到数据放入其对应的工作内存

2)将flag值更改为true,但是这个时候flag值还没有写回主内存

3)此时main方法读取到了flag的值为false

4)当子线程将flag的值写回去后,失效其他线程对此变量副本(volatile的作用)

5)再次对flag进行操作的时候线程会从主内存读取最多的值,放入到工作内存中。

总结:volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。

以上只是部分内容,为了维护方便,本文已迁移到新地址:volatile~多线程下变量不可见 – 编程屋

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值