• Java并发编程可见性问题


    1 计算机硬件中可见性问题

    1.1 可见性问题的来源

    单核CPU如果想要再通过提升频率会带来诸多问题凸显,如散热问题,能耗问题,技术难度很高,性能提升遇到瓶颈。改为采用多核cpu架构

    cpu运算速度比主存的存取速度快很多,为了提高处理速度,给每一个核增加高速缓存,cpu从高速缓存读取数据比直接从主存中读取数据快的多。通常多核架构的每个cpu都有他们自己独有的高速缓存:一级高速缓存L1(L1P+L1D),二级高速缓存L2。在同一个cpu芯片上的所有cpu还同时共享一个三级高速缓存L3。这样做的优点是:
    1.写缓存区可以保证指令流水线持续运行,可以避免由于cpu停顿下来等待向内存写入数据而产生的延迟。
    2.通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总线的占用。

    但是,随着多核cpu的使用也出现了一些需要解决的问题,如何保证主存中共享数据的并发问题。这里就出现了并发编程中的三大问题:原子性问题、可见性问题、有序性问题。这篇笔记主要理解一下可见性问题。

    可见性:一个线程对共享变量的修改,另一个线程能够立刻可见,我们称该共享变量具备内存可见性。

    可见性问题的产生:由于每个线程可能会运行在不同cpu内核中,因此每个线程拥有自己的高速缓存。同一份数据可能会被缓存到多个cpu内核中,在不同cpu内核中运行的线程看到同一个变量的缓存值就会不一样,就可能发生内存的可见性问题。

    1.2 可见性问题的解决

    于是为了保证每个cpu core访问数据的一致性,需要各个cpu在访问高速缓存时遵循一套缓存一致性协议。MESI(Modified、Exclusive、Shared、Invalid)就是一种最常用的缓存一致性协议。共享变量有上述四种状态,共享变量在每种状态下都可能遇到4种操作(本地读、本地写、远程读、远程写)。在每种状态下的操作都遵循MESI协议的规则就能够保证共享变量的可见性。其状态转换图如下:
    在这里插入图片描述
    状态转换规则如下:
    在这里插入图片描述

    2 Java中的可见性问题

    在Java中,Java程序是运行在JVM(Java Virtual Machine)中的。JVM中运行Java程序时,在使用内存遵循一套规则,我们叫做JMM(Java Memory Mode)。这一套规则用于解决可见性和有序性问题。
    JMM规定给每一个Java线程一个自己私有的工作内存(类比cpu高速缓存),当使用主存(类比cpu的内存)中的变量时,将主存中变量复制到工作内存中。所以这套内存模型与cpu高速缓存模型其实有相似之处,这里就不多讲JMM了。在JVM中和Java语言中包含了JMM的规则指令,将这些指令体现到Java的关键字语义中。
    所以在执行Java程序时,只有当共享变量被volatile关键字修饰了,该变量所在的缓存才被要求进行缓存一致性的校验。volatile关键字修饰共享变量后,会在操作该共享变量时添加lock前缀指令,该指令有以下三种功能:

    1. 将当前cpu缓存行的数据立即写回系统内存;
    2. lock前缀指令会引起在其他cpu中缓存了该内存地址的数据无效;
    3. lock前缀指令禁止指令重排序;
  • 相关阅读:
    9.2.5.2 【MySQL】XDES 类型
    OkHttp相关知识(二)
    Map集合的概述和接口的使用
    过采样、欠采样
    spring循环依赖的解决方式以及成功失败案例分析
    艾默生Emerson EDI需求分析
    Javaweb笔试题及机试题(附答案)
    C# - Opencv应用(1) 之VS下环境配置详解
    云计算项目十一:构建完整的日志分析平台
    1 jdbc连接池原理
  • 原文地址:https://blog.csdn.net/sixabs/article/details/127601168