JMM与volatile关键字

Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java的线程内存模型是标准化的,屏蔽了底层不同计算机的区别。
在这里插入图片描述

JMM数据原子操作

  • read(读取):从主内存读取数据
  • load(载入):将主内存读取到的数据写入工作内存中
  • user(使用):从工作内存读取数据来计算
  • assign(赋值):将计算好的值重新赋值到工作内存中
  • store(存储):将工作内存数据写入主内存
  • write(写入):将store过去的变量值赋值给主内存中的变量
  • lock(锁定):将主内存变量加锁,标识为线程独占状态
  • unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

以该例子为例

public class test {
    public static volatile boolean initFlag=false;
    public static void main(String[] args)  {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("waiting data ...");
                while (!initFlag){}
                System.out.println("success");
            }
        }).start();

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

    }
}

在这里插入图片描述

底层大致流程
在这里插入图片描述

JMM缓存不一致问题

总线加锁(性能太低)
cpu从主内存读取数据到高速缓存,会在总线对这个数据加锁,这样其他cpu没法去读或写这个数据,直到这个cpu使用完数据释放锁之后其他cpu才能读取该数据,即将并行程序变为串行执行,性能低。

MESI缓存一致性协议
多个cpu从主内存读取同一个数据到各自的高速缓存,当其中 某个cpu修改了缓存里的数据,该数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效

volatile可见性底层实现原理

volatile缓存可见性实现原理
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存(缓存行锁定)并回写到主内存中
IA-32架构软件开发者手册对lock指令的解释:
1.会将当前处理器缓存行的数据立即写回到系统内存。
2.这个写回内存的操作会引起在其他cpu里缓存了该内存地址的数据无效(MESI协议)

底层是c语言实现的 这个lock 关键字 中有一个锁 是加在store之前的 在完成write完成之后 解锁
在这里插入图片描述

即:共享变量值被修改之后 通过 汇编语言同步回主内存 经过主线的时候 会触发MESI缓存一致性协议 将其他线程的共享变量值 失效 去重新 read 共享变量值 同时 对主内存的赋值操作 加一把锁 放置 并发读 操作

原子性

public class volatiletest {
public static volatile int num=0;

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            System.out.println(i);
            threads[i]=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j <1000; j++) {
                        incrase();
                    }
                }
            });
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();//执行完此线程之后 启动其他线程
        }
        System.out.println(num);
    }

    private static void incrase() {
        num++;
    }
}

在这里插入图片描述
在这里插入图片描述
可以看到 它的结果是不同的

因为会产生 结果的丢失
因为 只有write和store 操作是加锁的 其他操作是不加的
当 线程1自增1了之后 线程2 抢到时间片 执行全部操作 执行到store 的时候 经过总线 通过MESI缓存一致性协议 使线程1的共享变量值 失效 导致 部分结果的丢失

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值