【JVM】从硬件层面和应用层面的有序性和可见性,到Java的volatile和synchronized

Java的关键字volatile保证了有序性和可见性,这里我试着从底层开始讲一下有序性和可见性。

一,一致性

数据如果同时被两个cpu读取了,如何保证数据的一致性?或者换句话说,cpu1改了数据,cpu2的数据就成了无效的数据,如何保证cpu读取的数据是有效的呢?

在计算机技术比较古老的时候,采用的解决办法就是总线锁,即锁住cpu到内存的cpu总线[3],如此,同时最多仅能有一个cpu访问数据,但是这样带来的坏处是显而易见的,就是并发能力很低。

在这里插入图片描述
另外一种比较现代的解决方式就是CPU缓存一致性协议,也可以叫缓存锁。最常见的是Intel实现的是MESI协议,MESI(Modified Exclusive Shared Or Invalid)(也称为伊利诺斯协议,是因为该协议由伊利诺斯州立大学提出)是一种广泛使用的支持写回策略的缓存一致性协议。CPU中每个缓存行(caceh line)使用4种状态进行标记(使用额外的两位(bit)表示),分别是[4]:

M: 被修改(Modified)
该缓存行只被缓存在该CPU的缓存中,并且是被修改过的(dirty),即与主存中的数据不一致,该缓存行中的内存需要在未来的某个时间点(允许其它CPU读取请主存中相应内存之前)写回(write back)主存。
当被写回主存之后,该缓存行的状态会变成独享(exclusive)状态。

E: 独享的(Exclusive)
该缓存行只被缓存在该CPU的缓存中,它是未被修改过的(clean),与主存中数据一致。该状态可以在任何时刻当有其它CPU读取该内存时变成共享状态(shared)。
同样地,当CPU修改该缓存行中内容时,该状态可以变成Modified状态。

S: 共享的(Shared)
该状态意味着该缓存行可能被多个CPU缓存,并且各个缓存中的数据与主存数据一致(clean),当有一个CPU修改该缓存行中,其它CPU中该缓存行可以被作废(变成无效状态(Invalid))。

I: 无效的(Invalid)
该缓存是无效的(可能有其它CPU修改了该缓存行)。

需要注意的是,这些标识了状态位的缓存行是存储在cpu私有的cpu高速缓存中(L1、L2)。可以看到,cpu根据缓存行的状态位进行判断,进而读取、或者重新从共享主存中读取、或者在别的cpu读取共享主存数据之前写入。

这样,我们通过缓存锁(实质上这里举例的是MESI协议),在某些情况下达到了确保一致性。

为什么是“有些情况”呢?因为在数据无法被缓存的情况下、或者数据跨越多个缓存行的情况下,依然需要使用总线锁来解决一致性问题。

二,有序性

说起保证有序性,在此之前,我想我们需要先看一下,如何证明,代码会被cpu乱序执行。网上有这样一段代码,可以证明。

    private static int x = 0, y = 0;
    private static int a = 0, b =0;

    public static void main(String[] args) throws InterruptedException {
   
        int i = 0;
        for(;;) {
   
            i++;
            x = 0; y = 0;
            a = 0; b = 0;
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值