Java内存模型系列(三)jvm如何实现内存模型

0x01 volatile关键字

作用一:保证变量可见性

volatile关键字修饰变量,保证此变量的写操作对所有线程的都是立即可见的。但是基于volatile的运算操作不是立即可见的(非线程安全)

特殊:以下两种场景需要要保证其操作的原子性

一:运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值
二:变量不需要与其他的状态变量共同参与不变约束

作用二:禁止指令重排序

在单线程中,所有代码都是被顺序执行的,因为就一个线程在执行这段代码,即时代码被调整了顺序,也没有其他线程因为这个被调整的顺序而产生错误。

通过代码举例说明

int i = 12;
boolean flag = false; 

场景:如果此时有多个线程在执行这段代码,都是根据flag的状态来判断是否开始,而i变量和flag变量的顺序可能flag变量先被赋值,那此时线程获取flagtrue那么就会开始执行i变量的操作,比如自增,那么此时对i进行操作就是错误的。

原理

通过内存屏障方式,即在编译指令中在赋值语句的下一句增加lock addl $0x0 (%esp)这样的lock操作来说明重排序时不能把后面的指令重排序到内存屏障之前的位置。虽然cpu有nop这样的空操作指令,但是它不允许和lock配合使用,所以每次lock都是把ESP寄存器+0。

lock前缀使得本CPU的Cache写入了内存,该写入动作会引起别的CPU或者别的内核缓存无效化(保证了可见性),相当于对Cache变量做了一次storewrite操作

lock指令把修改同步到内存时,意味着所有之前的操作都已经执行完成了

0x02 synchronized关键字

作用:可以解决原子性(加锁)、可见性(加锁)、有序性(as-if-serial)

实现原理

  • synchronized方法:通过ACC_SYNCHRONIZED标记符来实现同步
  • synchronized同步块:采用monitorentermonitorexit两个指令来实现

0x03 final关键字

如果没有发生this逃逸,那么final关键字能保证可见性,简单理解就是在任何线程访问某个对象的final域时,这个final域肯定被正确初始化了

什么是对象逃逸

在对象未完全初始化的时候,对象的引用被其他变量获取了

public class Foo {
    int i;
    static Foo foo;

    Foo() {
        i = 1; // 操作1
        foo = this; // 操作2
    }
}

上述代码中,操作1和操作2可能重排序,在单线程环境并无大碍,但是在多线程环境下,可能其他线程通过foo变量去获取i的值,但是由于重排序,可能i还没被赋值

0x04 as-if-serial语义

该语义保证了单线程环境下重排序之后程序执行结果的正确性

int i = 1; // 操作1
int j = 2; // 操作2
int f = i * j; // 操作3

由此代码中,操作1和操作2可以重排序,但是操作3是根据操作1和操作2的值决定的,不能重排序到最前面

0x05 happens-before语义

作用:可以解决可见性、有序性

jvm要保证重排序是符合happens-before语义的,所以后者对前者是可见的

规则:

  • 程序次序规则:在一个线程内,按照程序代码顺序,前面的操作先行发生于后面的操作
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作,同一个锁,后面指时间上的先后顺序
  • volatile变量规则:对一个volatile变量的写操作先行发生于读操作
  • 线程启动规则:Thread的start方法先行发生于此线程的每个动作
  • 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测
  • 线程中断规则:线程interrupt方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 对象终结规则:一个对象初始化完成(构造函数执行结束)先行发生于finalize方法
  • 传递性:操作A先行于操作B,操作B先行于操作C,则操作A先行于操作C

0xFF 总结

jmm封装了底层操作,提供更为高级的api帮助我们解决并发的原子性、可见性、有序性问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值