• 多线程《1》JMM基础知识---volatile的可见性和一致性



    前言:
    jvm是java虚拟机用于解释,编译执行java代码,jvm可以再不同的操作系统上执行并且提供了内存管理,垃圾回收等
    jmm是java中多线程对于内存共享的行为规范,规定了在多线程环境下如何正确的使用共享变量,jmm定义了变量的可见性,原子性和有序性等特征,

    引出线程的不可见性

    • 众所周知,电脑只会有一个主内存和多个cpu,多个cpu想拿到主内存的数据,并不是直接去访问主内存的,需要通过cpu缓存去拿主内存的数据
      图下所示:
      在这里插入图片描述
    • java多线程的内存标准和cpu缓存模型类似,是基于cpu缓存模型建立的
    • 如下图所示:主内存中存储的是isflag=true,每一个线程的工作内存会生成一个副本isflag=true
    • 如果线程1修改了值变为了isfalg=fase,那么他修改的对于另外一个线程是不可见的,另外一个线程取的是工作内存中的值
    • 线程1的工作内存是isflag=false 线程2的工作内存中的值是isflag==true,线程之间是不可见的
      在这里插入图片描述
    • 代码演示
    public class VolatileThread extends   Thread{
    
        public  boolean isflag=true;
    //public  volatile boolean isflag=true;
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"子线程开始执行...");
            while (isflag) {
    
            }
            System.out.println(Thread.currentThread().getName()+"子线程执行结束...");
        }
    
    
        public void setRuning(boolean  isflag)
        {
            System.out.println("修改值");
            this.isflag = isflag;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    public class ThreadVolatile {
        public static void main(String[] args) throws InterruptedException {
    
            //实例化
            VolatileThread volatileThread=new VolatileThread();
            //启动线程
            volatileThread.start();
    
            Thread.sleep(3000);
            //睡眠3s后,main线程调用修改状态的方法
            volatileThread.setRuning(false);
    
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 线程1初始值是true,然后顺利的进入的死循环,然后 volatileThread.setRuning(false); 修改为false了,正常情况下程序应该结束,但是Thread-0读取的是副本的值,并不是直接读取的主内存的值,所以main线程修改的值,他是不可见的,如何解决这个问题?将isflag用volatile修饰
      在这里插入图片描述

    jmm的八个原子性操作

    • 作可以确保多线程环境下的数据同步和一致性,保证对共享资源的操作是原子性的,避免了并发问题的发生。
    • read(r读取):从主内存中读取数据
    • load(载入)将主内存读取到的数据写入工作内存
    • use(使用)从工作内存读取数据来计算
    • assign(赋值)将计算好的值重新赋到工作变量中
    • store(存储)将工作内存数据写入到主内存
    • write(写入)将store过去的变量赋值给主内存
    • lock(锁定)将主内存变量加锁,标识位线程独占状态
    • unlock(解锁)将主内存变量解锁,解锁后其他线程可以锁定该变量
      在这里插入图片描述
      了解:
    • 缓存一致性协议
      多个cpu从主内存读取同一个数据到各自高速缓存当其中的某个cpu修改了数据该数据会马上同步回主内存,其他cpu会通过总线嗅探机制可以感知到数据的变化,从而改变自己工作区间的数据
    • 总线嗅探机制
      -总线嗅探机制是一种硬件机制,用于实现多个cpu共享数据的一致性,当一个cpu修改了数据并将其写入到了主内存中,其他cpu通过监听总线上的数据传输,可以感知到数据的变化。多个cpu从主内存读取同一个数据到各自高速缓存当其中的某个cpu修改了数据该数据会马上同步回主内存,其他cpu会通过总线嗅探机制可以感知到数据的变化,从而改变自己工作区间的数据
    • 思考:为什么线程2修改了数据,线程1会知道,并同步数据,其过程是什么样子的?
    • 原本isfllag-true 线程2通过store和write去修改isflag=false并写入到主内存中,需要经过数据总线,因为总线嗅探机制存在多个cpu去监控数据总线,当线程2修改数据经过数据总线的时候,被监控到了,其他cpu发现自己的工作空间也有isflag这个变量,就会失效,当use使用的时候,发现失效了,找不到了就会重新去主内存加载,这就是数据同步

    volatile 可见性

    • 使用volatile 会使缓存一致性生效,只有缓存一致性生效,其他cpu才可以快速感知到数据的变化,从而让自己副本的数据失效,重新前往主内存拿数据
    • 底层主要是通过汇编lock前缀指令,他会锁定这块区域的缓存(缓存行锁定),并写回主内存

    volatile 实现顺序一致性

    使用volatile的时候会加lock,在底层加上了lock的话就不会进行充排序了,这样就实现了顺序一致性(预计以后会写这个详细的)

  • 相关阅读:
    Android可滑动的分时图以及常用动画
    Qt-FFmpeg开发-视频播放(1)
    设计模式-原型模式
    【zabbix监控三】zabbix之部署代理服务器
    【大数据面试题】011 Hive的内部外部表
    【.NET】控制台应用程序的各种交互玩法
    spfa处理差分约束
    iframe与主窗口通信
    《Redis设计与实现》笔记
    Zephyr调度算法
  • 原文地址:https://blog.csdn.net/weixin_52457750/article/details/133273986