个人对volatile的一些理解

volatile关键字的作用

volatile用于修饰变量

  • 保证变量的内存可见性
  • 保证原子性
  • 禁止指令重排序

JMM模型

Java内存模型(Java Memory Model)是一种抽象的,本身不存在的一组规范,来定制程序中各个变量的访问方式.

JMM关于同步的规定
  • 线程解锁前,必须把共享变量的值写回主内存.
  • 线程加锁前,必须读取主内存中变量的最新值到自己工作内存.
  • 加锁与解锁必须使用同一把锁.
    由于JVM运行程序的载体是线程,而JVM创建每个线程时都会为该线程分配一块私有的工作内存(也称为栈内存).JMM规定所有变量都存储在主内存中,主内存是共享区域,所有线程都可以访问.但线程对变量的操作(读取赋值等)都必须在工作内存中进行,首先从主内存中读取变量到工作内存中,然后对该变量进行操作,操作完成把该变量写回主内存.各个线程的工作内存中存储着主内存变量的拷贝副本.不同线程无法访问对方的工作内存,因此必须通过主内存来完成.
    JMM线程操作变量

可见性

不同线程各自的内存空间里的变量是不能互相访问的,称为不可见性.
如果A线程修改了从主内存拷贝的一个变量,还未写回主内存,而B线程从主内存中读取了该变量,那么此变量的值就是A线程修改前的值,这就造成了AB两个线程的不可见问题.

原子性

原子性表示某个操作是不可拆分的.
比如i++操作,在底层被拆分成了三个操作:执行getfiled拿到原始的i,执行iadd进行+1操作,执行putfield操作把累加后的值写回.
如果有变量i初始值为0,线程A在工作内存中执行i++操作,首先拿到原始i,但还未执行+1操作时,线程B得到了cpu时间,线程A阻塞了.线程B在工作内存中也执行了i++操作,然后把i的值1写回了主内存.但是此时线程A不会再返回去读取主内存的i值,此时A线程工作内存中的i值还是0,A线程继续执行i++操作,然后把i的值1写回到主内存.此时主内存的i值就是1,B线程的操作就被A线程覆盖了.

有序性

为了尽可能的减少内存速度远慢于cpu速度所带来的cou闲置问题,虚拟机会按照自己的特定规则,将程序的编写顺序打乱执行,以尽可能的充分利用cpu,提高执行效率.这个前提是乱序执行不会导致程序结果出错.在单线程环境下,可以保证程序结果正确,但是在多线程环境下,重排序后的程序结果正确性是无法保证的.

volatile的实现原理

内存屏障(Memory Barrier),是一个cpu指令,作用有两个:

  • 保证特定操作的执行顺序.
  • 保证某些变量的内存可见性.
    编译器和处理器都可以执行指令重排序优化,如果在指令间插入一条内存屏障则会告诉编译器和cpu,不管什么指令都不能和这条内存屏障进行指令重排序,也就是通过插入内存屏障禁止在内存屏障的前后进行指令重排序.内存屏障另一个作用就是强制刷新出cpu的各种缓存数据,因此cpu上的所有线程都能读取到这些数据的最新值.
    在这里插入图片描述

volatile的使用场景

  • 单例模式DCL(双重检查锁)
class SingletonDemo2 {
    public volatile static SingletonDemo2 instance = null;

    private SingletonDemo2() {

    }

    public static SingletonDemo2 getInstance() {
        if (null == instance) {
            synchronized (SingletonDemo2.class) {
                if (null == instance) {
                    instance = new SingletonDemo2();
                }
            }
        }
        return instance;
    }
}

单例对象必须加volatile关键字的原因:
DCL不一定线程安全,因为有指令重排序的存在,加入volatile可以禁止指令重排序.初始化一个单例对象底层可分为以下步骤:

  • 分配内存空间
  • 初始化对象
  • 设置对象指向刚分配的内存地址
    步骤二和步骤三不存在数据依赖关系,而且指令重排后在单线程情况下不会改变程序的结果,因此这种重排优化是允许的.重排后会导致A线程拿到的instance不为null,但是此时instance未必已经完成初始化,也就造成了线程安全问题.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值