Java内存模型(JMM)底层原理

1、内存模型的概述

Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行。

首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

2、内存模型的示意图

主内存:

主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行访问可能会发现线程安全问题。

工作内存:

主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。注意由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,因此存储在工作内存的数据不存在线程安全问题。

3、JMM数据原子操作

4、内存模型工作流程图

上图jmm工作流程:

步骤一

线程1:先把initFlag变量从主内存中read读取出来,再load载入自己的工作内存,use使用线程1执行代码!initFlag。

步骤二

线程2:先把initFlag变量从主内存中read读取出来,再load载入自己的工作内存,use使用线程2执行代码initFlag=true,这时候值发生了改变,线程会再assign重新赋值到工作内存,然后store存储并写入主内存,write写入赋值到主内存中的变量。
(线程2对缓存行lock加锁,write写入主内存后会解锁unlock,防止initFlag还未write写入主内存就被线程1读取为false)。

步骤三

线程1:因为initFlag被volatile修饰,使用MESI缓存一致性协议,线程1利用cpu总线嗅探机制监听到了initFlag值的修改,线程1中工作内存initFlag=false失效,这时候线程就拿不到工作内存的数据了,从而工作内存继续去读取主内存数据,变为true退出循环继续执行,体现了多线程同步运行共享变量副本的可见性。如果initFlag没有被volatile修饰,线程1将感知不到initFlag的变化,一直循环下去停止不了。

实例代码:

public class VolatileVisibilityTest {
    //volatile变量,用来确保将变量的更新操作通知到其他线程。
    private static volatile boolean initFlag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("waiting data...");
                while (!initFlag) {

                }
                System.out.println("==============success");
            }
        }).start();

        Thread.sleep(2000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                prepareData();
            }
        }).start();
    }
        public static void prepareData(){
            System.out.println("preparing data...");
            initFlag = true;
            System.out.println("prepare data end...");
        }
}

以上加了“volatile”关键字,实现了共享变量的可见性问题

volatile是怎么实现的呢?原理是怎样的?

volatile底层原理不是Java来编写的,而是用汇编语言的来编写

 

很早以前是用总线的加锁的机制来做到可见性和一致性的

lock和unlock会对主内存加锁的,总线加锁一般不使用,效率太低,跟单线程差不多。一般用MESI缓存一致性协议。

https://www.infoq.cn/article/java-memory-model-1/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值