理解JMM内存模型与深入Volatile关键字原理

一、JMM内存模型

首先看一下CPU多核并发缓存架构,由于CPU和内存数据读写之间的差距过大,添加了缓存来缓和这个差距。
在这里插入图片描述
下面是JMM内存模型,每个线程能够从主内存读取共享变量,将读取到的共享变量保存在线程自己的工作内存中。需要注意的是线程与线程之间无法进行直接的通信。
在这里插入图片描述

二、深入理解Volatile关键字

一段代码如下,线程A和线程B共享变量initFlag。线程B先运行,线程A后运行。随后线程A将initFlag变为true,保存这个操作后主内存中的initFlag也会变为true。但是线程B不知道initFlag已经被改变了,线程B会一直执行不会停下。

public class VolatileVisibilitySample {

    //共享静态变量
    private static volatile boolean initFlag = false;

    public void refresh(){
        this.initFlag = true;
        String name = Thread.currentThread().getName();
        System.out.println(name+":修改了共享变量initFlag");
    }

    public void load(){
        String name = Thread.currentThread().getName();
        while(!initFlag){

        }
        System.out.println(name+":嗅探到了共享变量initFlag的改变");
    }

  public static void main(String[] args) {

      VolatileVisibilitySample sample = new VolatileVisibilitySample();

      Thread threadA = new Thread(()->{
          sample.refresh();
      },"线程A");

      Thread threadB = new Thread(()->{
          sample.load();
      },"线程B");

      threadB.start();

      try {
          Thread.sleep(100);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }

      threadA.start();

  }
}

在多线程的环境下,如果一个共享变量被被Volatile关键字修饰,那么在一个线程中改变了这个共享变量,其余的线程会受到改变的通知,并从主内存中读取新的值。当我们将initFlag用Volatile关键字修饰,线程A做的操作将会被线程B知道,线程B就会停下。实际上这个通知是通过MESI缓存一致性协议实现的,其本质是一个监听操作,如下图。当线程2将修改操作保存到主线程时,会通过总线,这里就会通知线程1,共享变量被改变了。

值得一提的是,在执行store和write这两个原子操作的时候,会有一个加锁和解锁的操作。
在这里插入图片描述
另外,在这里再详细说一下线程对共享变量的八个原子操作。如下:
在这里插入图片描述
多线程有三个原则:可见性、原子性、顺序性。显然Volatile关键字满足可见性,不过它不满足原子性,见下面代码:

public class VolatileAtomicSample {
    private static volatile int counter = 0;

  public static void main(String[] args) {

      for(int i=0;i<10;i++){
          Thread thread = new Thread(()->{
              for(int j=0;j<1000;j++){
                  //此处并不是原子操作
                  counter++;
              }
          });
          thread.start();
      }

      try {
          Thread.sleep(2000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }

      System.out.println(counter);

  }
}

理论上counter的值最后应该是10000,但是实际运行中counter会出现小于10000的情况。
在这里插入图片描述
说明在counter++时,有的自增操作被覆盖了,所以Volatile不满足原子性。

另外Volatile关键字满足有序性,在此不做验证。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值