Volatile禁止指令重排

以下截图及相关信息,均来源于马士兵公开课中


Volatile 禁止指令重排

CPU 存在乱序执行,Volatile 可以保证禁止指令重排(乱序执行)

一、Volatile 保证线程之间测试

测试代码:

volatile 关键字修饰与否,测试变量是否出现指令重排的结果

public class T01_Volatile_Rearrangement {
    private static int x = 0, y = 0;
    private static int a = 0, b = 0;

    /**
     * 可能出现的排列顺序:
     * a(1),x(0),b(1),y(1)      x:0  y:1
     * a(1),b(1),x(1),y(1)      x:1  y:1
     * a(1),b(1),y(0),x(1)      x:1  y:0
     * <p>
     * b(1),y(0),a(1),x(1)      x:1  y:0
     * b(1),a(1),y(1),x(1)      x:1  y:1
     * b(1),a(1),x(0),y(0)      x:0  y:1
     * <p>
     * 不可能出现的情况为: x:0  y:0
     * 除非出现这种情况:
     * x(0),y(0),a(1),b(1)
     * x(0),y(0),b(1),a(1)
     * y(0),x(0),a(1),b(1)
     * y(0),x(0),b(1),a(1)
     * <p>
     * 结论: x,y的执行在 a,b执行之前;说明了出现指令重排的情况
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        for (long i = 0; i < Long.MAX_VALUE; i++) {
            x = 0;
            y = 0;
            a = 0;
            b = 0;
            CountDownLatch latch = new CountDownLatch(2);

            Thread th1 = new Thread(() -> {
                a = 1;
                x = b;
                latch.countDown();
            });

            Thread th2 = new Thread(() -> {
                b = 1;
                y = a;
                latch.countDown();
            });


            th1.start();
            th2.start();
            latch.await();
            String result = "第" + i + "次(" + x + "," + y + ")";
            if (x == 0 && y == 0) {
                System.err.println(result);
                break;
            }
        }
    }
}

运行结果:

  • 当没有 volatile 关键字修饰时, 程序不会停止,因为不可能出现 x = 0 并且 y = 0 的情况;
  • 当有 volatile 关键字修饰时, 程序停止,输出 x = 0 并且 y = 0 的情况。

产生的原因:

发生了指令重排,才会出现 x = 0 并且 y = 0 的情况。

若不发生指令重排,不可能出现 x = 0 并且 y = 0 的情况。

二、Volatile 是如何禁止指令重排的?

实现过程

  1. Java代码层面:Volatile 关键字

  2. Java字节码层面:ACC_VOLATITLE

  3. JVM内存屏障:屏障两边的指令不可以重排序,保障有序!

  4. hotspot 实现:

    bytecodeinterpreter.cpp

    int field_offset = cache->f2_as_index();
               if (cache->is_volatile()) {
                 if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
                   OrderAccess::fence();
                 }
    

    orderaccess_linux_x86.inline.hpp

    inline void OrderAccess::fence() {
       if (os::is_MP()) {
         // always use locked addl since mfence is sometimes expensive
     #ifdef AMD64
         __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
     #else
         __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
     #endif
       }
     }
    

底层实现两种方式:

1.内存屏障

内存屏障sfence mfence lfence等系统原语

2.锁总线

内存屏障介绍

屏障指的是什么?屏障指的是一种特殊的指令(例如:barrier),只要看到 barrier 就不让前后指令交换顺序,这就是使用屏障禁止指令重排。

屏障在底层角度:从CPU角度,每种CPU的屏障指令是不一样的,

例如:Intel(英特尔)

CPU内存屏障,Intel设计得比较简单,总共只有3条指令:

①sfence:也就是save fence,写屏障指令。在sfence指令前的写操作必须在sfence指令后的写操作前完成。

②lfence:也就是load fence,读屏障指令。在lfence指令前的读操作必须在lfence指令后的读操作前完成。

③mfence:在mfence指令前得读写操作必须在mfence指令后的读写操作前完成。

Java是跨平台的语言,Java怎么保证在不同的平台,都能够实现呢

JVM定义规范,定义无论是Linux上的JVM,还是Window上的JVM,还是在Intel CPU上的JVM,我只要使用了Volatile 关键词,哪一个都不允许指令交换顺序。

JVM内存屏障

无论哪种CPU或者哪种系统上的JVM必须实现这四种屏障;

LoadLoad屏障:

对于这样的语句:Load1;LoadLoad;Load2

在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据读取完毕。

即: Load1 读取数据完毕后, Load2才能读取数据

StoreStore屏障:

对于这样的语句:Store1; StoreStore; Store2

在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

即:Store1写入操作完毕后, Store2才能进行写入操作

LoadStore屏障:

对于这样的语句: Load1; LoadStore; Store2

在Store2及后续代码写入操作被刷出前,保证Load1读取的数据读取完毕。

即:Load1 读取数据完毕后, Store2才能进行写入操作

StoreLoad屏障:

对于这样的语句:Store1; StoreLoad; Load2

在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

即:Store1写入操作完毕后, Load2才能读取数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容