volatile用法

volatile的作用

首先,volatile关键字只能修饰变量,不能修饰方法或者代码块

volatile有两个作用:

  • 保证线程的可见性:一个线程对volatile变量的修改,会立刻反映到其他线程中,其他线程能够立刻看到这个变量的最新值,避免出现多线程中变量数据不一致的问题
  • 禁止指令重排序volatile变量的写操作之前的所有操作,即使重排序,会被重排序到写操作之前;而写操作之后的所有操作,即使重排序,也只会重排序到写操作之后。保证了多线程操作的逻辑正确性

为什么会指令重排序?

白话就是“实际执行的代码顺序和写的代码顺序不一样

原因:指令重排序是由编译器、处理器(CPU)或多线程调度器在优化程序执行效率时进行的一种操作,改变了代码中的某些指令的执行顺序但不会更改代码的逻辑顺序)。

image-20241105163613068

特点:

  • 单线程中,即使指令重排序,因为不会改变逻辑顺序(依赖关系),所以不会对结果产生影响,只会提升效率
  • 多线程中,由于在不同线程之间存在共享资源,指令重排序改变了程序中的某些操作的执行顺序,可能导致不同线程之间的操作不按预期顺序进行的问题。

volatile如何禁止指令重排序?

更严格地说,应该叫做“volatile禁止指定变量参与指令重排序

原理:volatile通过内存屏障来禁止指令重排序

JVM内存屏障地策略

  • 在每个volatile写操作的前面插入一个StoreStore屏障。
  • 在每个volatile写操作的后面插入一个StoreLoad屏障。
  • 在每个volatile读操作的前面插入一个LoadLoad屏障。
  • 在每个volatile读操作的后面插入一个LoadStore屏障。

image-20241105164821128

这个原理流程图我还没琢磨透,但是简而言之,按照如下意思理解就可以了:

原本代码块都会被指令重排,但是如果一个变量被volatile修饰了,在编译和处理器阶段(即发生指令重排序的时候),将会分为三部分:

  • volatile之前的代码
  • volatile修饰的变量的代码
  • volatile之后的代码;
  • 其中①和③分别进行指令重排序(前面的代码不会重拍到后面,后面的代码不会重排到前面),这样就避免了重排序导致的多线程逻辑错误
Contents
  1. 1. volatile的作用
  2. 2. 为什么会指令重排序?
  3. 3. volatile如何禁止指令重排序?
|