zoukankan      html  css  js  c++  java
  • JMM 内存模型 与 volatile 关键字

    内存模型

    线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory)。
    
        本地内存中存储了该线程以读/写共享变量的副本。
    
        不同线程之间无法相互直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成。
    
    
    计算机运行过程中数据都是存放在主内存中的:
        由于CPU的执行速度非常的快,数据的读取和写入的速度相对较慢,导致CPU执行效率也会大大的降低
    
    当程序在运行过程中,会将运算需要的数据从主内存复制一份到高速缓存中:
        后续直接通过高速CPU缓存对数据进行写和读这样,大大的提高了程序的运行速度
    

    可见性 原子性 有序性

    可见性:
        可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
    
    原子性:
        不可分割
    
    有序性:
        程序执行的顺序按照代码的先后顺序执行
    
        synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
    
        volatile关键字来保证一定的“有序性”(防止指令重排)
    
    
    volatile、synchronized 和 final 实现可见性。
    
    synchronized 和在 lock、unlock 中操作保证原子性。
    
    volatile 和 synchronized 两个关键字来保证线程之间操作的有序性。
    

    volatile 关键字

    private volatile int account = 100;
    
    可见性:
    
        b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
    
        c.因此每次使用该域就要重新计算,而不是使用寄存器中的值 
    
        d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
    
    
    不能保证它具有原子性(解决:使用AtomicInteger(推荐)或者synchronized ):
    
        比如 volatile int a = 0;之后有一个操作 a++;
    
        这个变量a具有可见性,但是a++ 依然是一个非原子操作 
    
        不会提供任何原子操作,它也不能用来修饰final类型的变量
    
    
    禁止指令重排:
    
    synchronized关键字的确能保证可见性,为什么还需要volatile关键字保证可见性?(单例模式中双重验证的指令重排问题)
    
      Singleton singleton = new Singleton()时,这个过程是分步骤的并不是一个原子性的操作,实例化对象其实可以被分为三步:
    
        1、分配内存空间:memory = allocate();
    
        2、初始化对象:initInstance(memory);
    
        3、将instance指向刚刚分配的内存空间:instance = memory
    
      操作2依赖于操作1,但是操作3并不依赖于操作2,2和3步是可以进行重排序的:
    
        memory = allocate();    //1:分配对象的内存空间
        instance = memory;      //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
        ctorInstance(memory);   //2:初始化对象
    
        在多线程的情况下当一个线程进入执行new操作,其它线程返回的可能是一个还未初始化完全的对象。
    
        其它线程还是可以进行getInstance()方法(错以为Instance已经被实例化出来),
        当new操作执行还未全执行完时,其它线程拿到的就是未实例化完全的对象,造成线程不安全。
    
    volatile关键字就能保证是一个完全初始化后的对象(禁止指令重排)。
    

    内存屏障(Memory Barrier)

    内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:
        1、保证特定操作的执行顺序。
        2、影响某些数据(或者是某条指令的执行结果)的内存可见性。
    
    插入一条Memory Barrier会告诉编译器和CPU:
        不管什么指令都不能和这条Memory Barrier指令重排序。
    
    Memory Barrier所做的另外一件事是强制刷出各种CPU cache:
        如一个Write-Barrier(写入屏障)将刷出所有在 Barrier 之前写入 cache 的数据,
        因此,任何CPU上的线程都能读取到这些数据的最新版本。
    
    volatile是基于Memory Barrier实现的。
    

  • 相关阅读:
    第一次作业
    第0次作业—姚舜禹17-1
    第三周作业
    第二周作业
    第一周作业
    第零周作业
    第三周作业
    第二周作业
    第一周作业
    第0次作业
  • 原文地址:https://www.cnblogs.com/loveer/p/11409425.html
Copyright © 2011-2022 走看看