Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • Java核心基础

    • 基础篇

    • 集合类

    • JVM虚拟机

    • Java并发

      • 产品经理问我:手动创建线程不香吗,为什么非要用线程池呢?
      • 面试官:我问的是Java内存模型,你回答堆栈方法区干嘛?
      • 指令重排序、内存屏障很难?看完这篇你就懂了!
        • (一)什么是指令重排序
        • (二)复现指令重排序
        • (三)通过什么方式禁止指令重排序?
      • Volatile只会用不知道原理?一篇文章带你深究volatile
      • 有关synchronized锁的知识点,我用一篇文章总结了
      • 面试被问AQS、ReentrantLock答不出来?这些知识点让我和面试官聊了半小时!
      • 大厂面试题:你知道JUC中的Semaphore、CyclicBarrier、CountDownLatch吗
      • 关于ThreadLocal的九个知识点,看完别再说不懂了!
  • 框架的艺术

  • 分布式与微服务

  • 开发经验大全

  • 版本新特性

  • Java
  • Java核心基础
  • Java并发
CodeEase
2023-09-19
目录

指令重排序、内存屏障很难?看完这篇你就懂了!

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

面试官在问到多线程编程的时候,指令重排序、内存屏障经常会被提起。如果你对这两者有一定的理解,那这就是你的加分项。

# (一)什么是指令重排序

为了使处理器内部的运算单元能尽量被充分利用,处理器可能会对输入的代码进行乱序执行优化,处理器会在计算之后将乱序执行的结果重组,并确保这一结果和顺序执行结果是一致的,但是这个过程并不保证各个语句计算的先后顺序和输入代码中的顺序一致。这就是指令重排序。

简单来说,就是指你在程序中写的代码,在执行时并不一定按照写的顺序。

在Java中,JVM能够根据处理器特性(CPU多级缓存系统、多核处理器等)适当对机器指令进行重排序,最大限度发挥机器性能。

Java中的指令重排序有两次,第一次发生在将字节码编译成机器码的阶段,第二次发生在CPU执行的时候,也会适当对指令进行重排。

# (二)复现指令重排序

光靠说不容易看出现象,下面来看一段代码,这段代码网上出现好多次了,但确实很能复现出指令重排序。我把解释放在代码后面。

public class VolatileReOrderSample {
    //定义四个静态变量
    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;
        while (true){
            i++;
            x=0;y=0;a=0;b=0;
            //开两个线程,第一个线程执行a=1;x=b;第二个线程执行b=1;y=a
            Thread thread1=new Thread(new Runnable() {
                @Override
                public void run() {
                    //线程1会比线程2先执行,因此用nanoTime让线程1等待线程2 0.01毫秒
                    shortWait(10000);
                    a=1;
                    x=b;
                }
            });
            Thread thread2=new Thread(new Runnable() {
                @Override
                public void run() {
                    b=1;
                    y=a;
                }
            });
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            //等两个线程都执行完毕后拼接结果
            String result="第"+i+"次执行x="+x+"y="+y;
            //如果x=0且y=0,则跳出循环
            if (x==0&&y==0){
                System.out.println(result);
                break;
            }else{
                System.out.println(result);
            }
        }
    }
    //等待interval纳秒
    private static void shortWait(long interval) {
        long start=System.nanoTime();
        long end;
        do {
            end=System.nanoTime();
        }while (start+interval>=end);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

这段代码虽然看着长,其实很简单,定义四个静态变量x,y,a,b,每次循环时让他们都等于0,接着用两个线程,第一个线程执行a=1;x=b;第二个线程执行b=1;y=a。

这段程序有几个结果呢?从逻辑上来讲,应该有3个结果:

当第一个线程执行到a=1的时候,第二个线程执行到了b=1,最后x=1,y=1;

当第一个线程执行完,第二个线程才刚开始,最后x=0,y=1;

当第二个线程执行完,第一个线程才开始,最后x=1,y=0;

理论上无论怎么样都不可能x=0,y=0;

但是当程序执行到几万次之后,竟然出现了00的结果:

3-1.png

这就是因为指令被重排序了,x=b先于a=1执行,y=a先于b=1执行。

3-2.png

# (三)通过什么方式禁止指令重排序?

Volatile通过内存屏障可以禁止指令重排序,内存屏障是一个CPU的指令,它可以保证特定操作的执行顺序。

内存屏障分为四种:

StoreStore屏障、StoreLoad屏障、LoadLoad屏障、LoadStore屏障。

JMM针对编译器制定了Volatile重排序的规则:

3-3.png

光看这些理论可能不容易懂,下面我就用通俗的话语来解释一下:

首先是对四种内存屏障的理解,Store相当于是写屏障,Load相当于是读屏障。

比如有两行代码,a=1;x=b;并且我把 a 和 b 修饰为 volatile。

执行 a=1 时,它相当于执行了一次 volatile 写操作;

执行 x=b 时,它相当于先执行 volatile 读取 b,再执行普通写 x 等于 b;

因此在这两行命令之间,就会插入一个 StoreLoad 屏障(前面是写后面也是写),这就是内存屏障。

第一个操作是 volatile 写操作,第二个操作是 volatile 读操作,那么表格中对应的值就是 NO,禁止重排序。这就是 Volatile 进行指令重排序的原理。

现在,只需要把上面代码的 a 和 b 用 volatile 修饰,就不会发生指令重排序了。(如果你能通过表推一遍逻辑,你就能懂了)。

上次更新: 2025/04/29, 17:22:06
面试官:我问的是Java内存模型,你回答堆栈方法区干嘛?
Volatile只会用不知道原理?一篇文章带你深究volatile

← 面试官:我问的是Java内存模型,你回答堆栈方法区干嘛? Volatile只会用不知道原理?一篇文章带你深究volatile→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式