volatile 关键字详解

volatile关键字理解 这里的“可见性”指的是,当对一个变量修改后,新值对于其他线程是立即可见的,注意这里的立即可见,并不是说其他线程能监听到变量值修改,而是说修改后的值能立即同步到主内存中(稍后介绍Java内存模型),保证其他线程能读取到的一定是最新值。什么是指令重排优化呢,Java源代码最终会编译成计算机能识别的机器码指令,为了提高执行效率,编译器和处理器可能会对指令进行优化重新排序,导致实际上指令执行的顺序可能会和源代码中想表达的顺序不一致。到此如果能轻松的理解上面两句话的,可以跳过本文。 阅读详情



爆赞



volatile 的作用

大家都应该知道 volatile 的主要作用有两点:- 保证变量的内存可见性 - 禁止指令重排序
那么,什么是内存可见性,什么是指令重排序,以及它们涉及了那些机制呢?下面就让我们来看看吧。
在这里提醒一下,各位小伙伴要有个心理准备,就一个 volatile 关键字所涉及的知识点超乎你的想象哟。

可见性问题

在理解 volatile 的内存可见性前,我们先来看看这个比较常见的多线程访问共享变量的例子。

/**
 * 变量的内存可见性例子
 *
 * @author star
 */
public class VolatileExample {

    /**
     * main 方法作为一个主线程
     */
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        // 开启线程
        myThread.start();

        // 主线程执行
        for (; ; ) {
            if (myThread.isFlag()) {
                System.out.println("主线程访问到 flag 变量");
            }
        }
    }

}

/**
 * 子线程类
 */
class MyThread extends Thread {

    private boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 修改变量值
        flag = true;
        System.out.println("flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

执行上面的程序,你会发现,控制台永远都不会输出 “主线程访问到 flag 变量” 这句话。我们可以看到,子线程执行时已经将 flag 设置成 true,但主线程执行时没有读到 flag 的最新值,导致控制台没有输出上面的句子。
那么,我们思考一下为什么会出现这种情况呢?这里我们就要了解一下 Java 内存模型(简称 JMM)。

Java 内存模型

JMM(Java Memory Model):Java 内存模型,是 Java 虚拟机规范中所定义的一种内存模型,Java 内存模型是标准化的,屏蔽掉了底层不同计算机的区别。也就是说,JMM 是 JVM 中定义的一种并发编程的底层模型机制。
JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

JMM 的规定:

所有的共享变量都存储于主内存。这里所说的变量指的是实例变量和类变量,不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。
每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。
线程对变量的所有的操作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量。
不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存中转来完成。
JMM 的抽象示意图:
在这里插入图片描述

然而,JMM 这样的规定可能会导致线程对共享变量的修改没有即时更新到主内存,或者线程没能够即时将共享变量的最新值同步到工作内存中,从而使得线程在使用共享变量的值时,该值并不是最新的。
正因为 JMM 这样的机制,就出现了可见性问题。也就是我们上面那个例子出现的问题。
那我们要如何解决可见性问题呢?接下来我们就聊聊内存可见性以及可见性问题的解决方案。

内存可见性

内存可见性是指当一个线程修改了某个变量的值,其它线程总是能知道这个变量变化。也就是说,如果线程 A 修改了共享变量 V 的值,那么线程 B 在使用 V 的值时,能立即读到 V 的最新值。

可见性问题的解决方案

我们如何保证多线程下共享变量的可见性呢?也就是当一个线程修改了某个值后,对其他线程是可见的。
这里有两种方案:加锁 和 使用 volatile 关键字。
下面我们使用这两个方案对上面的例子进行改造。

加锁

使用 synchronizer 进行加锁。

/**
  * main 方法作为一个主线程
  */
  public static void main(String[] args) {
      MyThread myThread = new MyThread();
      // 开启线程
      myThread.start();

      // 主线程执行
      for (; ; ) {
          synchronized (myThread) {
              if (myThread.isFlag()) {
                  System.out.println("主线程访问到 flag 变量");
                }
          }
      }
  }

这里大家应该有个疑问是,为什么加锁后就保证了变量的内存可见性了? 因为当一个线程进入 synchronizer 代码块后,线程获取到锁,会清空本地内存,然后从主内存中拷贝共享变量的最新值到本地内存作为副本,执行代码,又将修改后的副本值刷新到主内存中,最后线程释放锁。
这里除了 synchronizer 外,其它锁也能保证变量的内存可见性。

使用 volatile 关键字

使用 volatile 关键字修饰共享变量。

/**
 * 子线程类
 */
class MyThread extends Thread {

    private volatile boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 修改变量值
        flag = true;
        System.out.println("flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

使用 volatile 修饰共享变量后,每个线程要操作变量时会从主内存中将变量拷贝到本地内存作为副本,当线程操作变量副本并写回主内存后,会通过 CPU 总线嗅探机制告知其他线程该变量副本已经失效,需要重新从主内存中读取。
volatile 保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值。
接下来我们就聊聊一个比较底层的知识点:总线嗅探机制。

总线嗅探机制

在现代计算机中,CPU 的速度是极高的,如果 CPU 需要存取数据时都直接与内存打交道,在存取过程中,CPU 将一直空闲,这是一种极大的浪费,所以,为了提高处理速度,CPU 不直接和内存进行通信,而是在 CPU 与内存之间加入很多寄存器,多级缓存,它们比内存的存取速度高得多,这样就解决了 CPU 运算速度和内存读取速度不一致问题。
由于 CPU 与内存之间加入了缓存,在进行数据操作时,先将数据从内存拷贝到缓存中,CPU 直接操作的是缓存中的数据。但在多处理器下,将可能导致各自的缓存数据不一致(这也是可见性问题的由来),为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,而嗅探是实现缓存一致性的常见机制。
在这里插入图片描述

处理器内存模型

注意,缓存的一致性问题,不是多处理器导致,而是多缓存导致的。

嗅探机制工作原理:每个处理器通过监听在总线上传播的数据来检查自己的缓存值是不是过期了,如果处理器发现自己缓存行对应的内存地址修改,就会将当前处理器的缓存行设置无效状态,当处理器对这个数据进行修改操作的时候,会重新从主内存中把数据读到处理器缓存中。

注意:基于 CPU 缓存一致性协议,JVM 实现了 volatile 的可见性,但由于总线嗅探机制,会不断的监听总线,如果大量使用
volatile 会引起总线风暴。所以,volatile 的使用要适合具体场景。

可见性问题小结

上面的例子中,我们看到,使用 volatile 和 synchronized 锁都可以保证共享变量的可见性。相比 synchronized 而言,volatile 可以看作是一个轻量级锁,所以使用 volatile 的成本更低,因为它不会引起线程上下文的切换和调度。但 volatile 无法像 synchronized 一样保证操作的原子性。

下面我们来聊聊 volatile 的原子性问题。

volatile 的原子性问题

所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。
在多线程环境下,volatile 关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性。也就是说,多线程环境下,使用 volatile 修饰的变量是线程不安全的。
要解决这个问题,我们可以使用锁机制,或者使用原子类(如 AtomicInteger)。
这里特别说一下,对任意单个使用 volatile 修饰的变量的读 / 写是具有原子性,但类似于 flag = !flag 这种复合操作不具有原子性。简单地说就是,单纯的赋值操作是原子性的。

禁止指令重排序

什么是重排序?
为了提高性能,在遵守 as-if-serial 语义(即不管怎么重排序,单线程下程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守。)的情况下,编译器和处理器常常会对指令做重排序。
一般重排序可以分为如下三种类型:

  • 编译器优化重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  • 指令级并行重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  • 内存系统重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

数据依赖性:如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

从 Java 源代码到最终执行的指令序列,会分别经历下面三种重排序:
在这里插入图片描述

重排序顺序
为了更好地理解重排序,请看下面的部分示例代码:

int a = 0;
// 线程 A
a = 1;           // 1
flag = true;     // 2

// 线程 B
if (flag) { // 3
  int i = a; // 4
}

单看上面的程序好像没有问题,最后 i 的值是 1。但是为了提高性能,编译器和处理器常常会在不改变数据依赖的情况下对指令做重排序。假设线程 A 在执行时被重排序成先执行代码 2,再执行代码 1;而线程 B 在线程 A 执行完代码 2 后,读取了 flag 变量。

由于条件判断为真,线程 B 将读取变量 a。此时,变量 a 还根本没有被线程 A 写入,那么 i 最后的值是 0,导致执行结果不正确。那么如何程序执行结果正确呢?这里仍然可以使用 volatile 关键字。

这个例子中, 使用 volatile 不仅保证了变量的内存可见性,还禁止了指令的重排序,即保证了 volatile 修饰的变量编译后的顺序与程序的执行顺序一样。那么使用 volatile 修饰 flag 变量后,在线程 A 中,保证了代码 1 的执行顺序一定在代码 2 之前。

那么,让我们继续往下探索, volatile 是如何禁止指令重排序的呢?这里我们将引出一个概念:内存屏障指令

内存屏障指令

为了实现 volatile 内存语义(即内存可见性),JMM 会限制特定类型的编译器和处理器重排序。为此,JMM 针对编译器制定了 volatile 重排序规则表,如下所示:
在这里插入图片描述

使用 volatile 修饰变量时,根据 volatile 重排序规则表,Java 编译器在生成字节码时,会在指令序列中插入内存屏障指令来禁止特定类型的处理器重排序。

内存屏障是一组处理器指令,它的作用是禁止指令重排序和解决内存可见性的问题。
JMM 把内存屏障指令分为下列四类:
在这里插入图片描述

StoreLoad
屏障是一个全能型的屏障,它同时具有其他三个屏障的效果。所以执行该屏障开销会很大,因为它使处理器要把缓存中的数据全部刷新到内存中。

下面我们来看看 volatile 读 / 写时是如何插入内存屏障的,见下图:
在这里插入图片描述

从上图,我们可以知道 volatile 读 / 写插入内存屏障规则:

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

也就是说,编译器不会对 volatile 读与 volatile 读后面的任意内存操作重排序;编译器不会对 volatile 写与 volatile 写前面的任意内存操作重排序。

happens-before 概述

上面我们讲述了重排序原则,为了提高处理速度, JVM 会对代码进行编译优化,也就是指令重排序优化,但是并发编程下指令重排序也会带来一些安全隐患:如指令重排序导致的多个线程操作之间的不可见性。为了理解 JMM 提供的内存可见性保证,让程序员再去学习复杂的重排序规则以及这些规则的具体实现,那么程序员的负担就太重了,严重影响了并发编程的效率。

所以从 JDK5 开始,提出了 happens-before 的概念,通过这个概念来阐述操作之间的内存可见性。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。

happens-before 规则如下:

  • 程序顺序规则: 一个线程中的每个操作,happens-before 于该线程中的任意后续操作。
  • 监视器锁规则: 对一个监视器锁的解锁,happens-before 于随后对这个监视器锁的加锁。
  • volatile 变量规则: 对一个 volatile 域的写,happens-before 于任意后续对这个 volatile 域的读。
  • 传递性: 如果 A happens-before B,且 B happens-before C,那么 A happens-before C。
  • start() 规则: Thread.start() 的调用会 happens-before 于启动线程里面的动作。
  • join() 规则: Thread 中的所有动作都 happens-before 于其他线程从 Thread.join() 中成功返回。

这里特别说明一下,happens-before 规则不是描述实际操作的先后顺序,它是用来描述可见性的一种规则。

从 happens-before 的 volatile 变量规则可知,如果线程 A 写入了 volatile 修饰的变量 V,接着线程 B 读取了变量 V,那么,线程 A 写入变量 V 及之前的写操作都对线程 B 可见。

volatile 在单例模式中的应用

单例模式有 8 种,而懒汉式单例双重检测模式中就使用到了 volatile 关键字。
代码如下:

public class Singleton {
    // volatile 保证可见性和禁止指令重排序
    private static volatile Singleton singleton;

    public static Singleton getInstance() {
        // 第一次检查
        if (singleton == null) {
          // 同步代码块
          synchronized(this.getClass()) {
              // 第二次检查
              if (singleton == null) {
                    // 对象的实例化是一个非原子性操作
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

上面代码中, new Singleton() 是一个非原子性操作,对象实例化分为三步操作:

  1. 分配内存空间
  2. 初始化实例
  3. 返回内存地址给引用。

所以,在使用构造器创建对象时,编译器可能会进行指令重排序。假设线程 A 在执行创建对象时,(2)和(3)进行了重排序,如果线程 B 在线程 A 执行(3)时拿到了引用地址,并在第一个检查中判断 singleton != null 了,但此时线程 B 拿到的不是一个完整的对象,在使用对象进行操作时就会出现问题。

所以,这里使用 volatile 修饰 singleton 变量,就是为了禁止在实例化对象时进行指令重排序。

总结

  • volatile 修饰符适用于以下场景:某个属性被多个线程共享,其中有一个线程修改了此属性,其他线程可以立即得到修改后的值;或者作为状态变量,如 flag = ture,实现轻量级同步。
  • volatile 属性的读写操作都是无锁的,它不能替代 synchronized,因为它没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁上,所以说它是低成本的。
  • volatile 只能作用于属性,我们用 volatile 修饰属性,这样编译器就不会对这个属性做指令重排序。
  • volatile 提供了可见性,任何一个线程对其的修改将立马对其他线程可见。volatile 属性不会被线程缓存,始终从主存中读取。
  • volatile 提供了 happens-before 保证,对 volatile 变量 V 的写入 happens-before 所有其他线程后续对 V 的读操作。
  • volatile 可以使纯赋值操作是原子的,如 boolean flag = true; falg = false。
  • volatile 可以在单例双重检查中实现可见性和禁止指令重排序,从而保证安全性。
线程安全问题 --- 内存可见性问题 所谓内存可见性, 就是当两个线程同时访问一个变量, 其中一个变量发生变化时, 另一个线程能够及时获取该变量的最新值,注意区分这里的内存可见性和之前的脏读, 不可重复读那么内存安全性问题便是当两个线程同时访问一个变量, 其中一个变量发生变化时, 另一个线程无法获取该变量最新值, 这个问题要使用volatile来解决。 阅读详情

相关推荐

【线程安全】内存可见性问题及解决方案

本期主要讲解: 1.关于内存可见性的一段代码 2.volatile关键字 3.JVM官方内存模型 4.volatile不保证原子性

程序猿教你打篮球的博客 5307

volatile 关键字,你真的理解吗?

这里我们将引出一个概念:内存屏障指令 内存屏障指令 为了实现 volatile 内存语义(即内存可见性),JMM 会限制特定类型的编译器和处理器重排序。为此,JMM 针对编译器制定了 volatile 重排序规则表,如下所示: 使用 volatile 修饰变量时,根据 volatile 重排序规则表,Java 编译器在生成字节码时,会在指令序列中插入内存屏障指令来禁止特定类型的处理器重排序。 内...

Star's Tech Blog 2610

从ABS到EBS再到AEBS:商用车制动安全系统的“三代同堂”与技术演进史

本文详细解析了商用车制动安全系统从ABS到EBS再到AEBS的技术演进历程。通过对比三代系统的性能差异和应用效果,揭示了电子控制单元(ECU)在提升制动精度和安全性的核心作用,并展望了智能制动技术的未来发展趋势。

weixin_42651748的博客 329

volatile关键字 -- 保证内存可见性、禁止指令重排序

简单介绍什么是内存可见性和指令重排序。 volatile关键字可以将这两种编译器优化强制关闭。

想听风雨的博客 1104

Power BI常见问题篇1:无法打开文档

Power BI常见问题篇1:无法打开文档

weixin_48273257的博客 3304

从ST转国产ARM必看:GD32/APM32等5款芯片的SWD下载全攻略(含CH32特殊处理)

本文为从STM32转向国产ARM芯片的开发者提供了详细的SWD下载调试指南。针对GD32F103C8T6、APM32F103C8T6等五款热门国产芯片,详细说明了如何利用已有的ST-Link工具和Keil MDK环境进行程序烧录,并特别介绍了CH32F103芯片所需的独立器件库安装等特殊处理方法,帮助开发者实现平滑迁移,提升开发效率。

weixin_29195615的博客 278

volatile关键字详解

缓存一致性协议: 每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。在多线程环境下,CPU轮流切换线程执行,有可能某个线程修改了数据,准备回主内存,此时CPU切换到另一个线程修改了数据,并且写回到了主内存,此时就导致数据的不准确。的作用就是,当某个线程工作内存中的值与主内存中的值,如果不相同就会一直。

努力分享一些干货 1837

BEVFormer详解

BEVFormer 是将Transformer架构的自注意机制与BEV视图中3D检测结合起来的一种纯视觉目标检测方案。对于纯视觉的BEV检测方案,其中的重中之重就是如何将2D的图像特征映射到3D空间的BEV栅格,既然是映射关系,那就有前行投影和反向查询两种机制。前向投影是基于深度估计的方法,参考基于深度估计的BEV视图转换方法,典型代表为LSS。反向查询方法思路为先将BEV栅格在Z方向上进行lift提升,然后再映射到图像特征图上进行特征查询。BEVFormer 就是基于这种机制进行2D图像特征到3D空间的B

MAX的专栏 4744

C#关键字volatile

在这个例子中,count字段被声明为volatile。在这里,hasData变量被声明为volatile,确保了生产者线程对hasData的修改能够被消费者线程及时看到,并且消费者线程对hasData的修改也能被生产者线程及时看到,从而保证了生产者 - 消费者模式的正确运行。例如,有两个线程,一个线程(线程 A)在不断地更新一个共享变量的值,另一个线程(线程 B)在读取这个共享变量的值。因为它要求对变量的每次访问都直接从主内存中进行,而不是从线程的工作内存(缓存)中进行,这会增加内存访问的开销。

2401_83367969的博客 2148

HFSS实战项目——高带外抑制特性SIW带通滤波器

本文对单级滤波器和双级滤波器的设计、工作原理及仿真结果进行了详细分析,并验证了双级SIW带通滤波器在性能方面的优越性,为微波通信系统中滤波器的设计和应用提供了重要参考。

2401_87464239的博客 1812

PSM倾向性评分分析介绍、spss进行倾向性评分分析示例

PSM倾向性评分分析介绍、spss进行倾向性评分分析示例

statistics+insight+vista+power 1481

java volatile 关键字详解

java volatile 关键字详解 一,什么是volatile关键字,作用是什么 ​ volatile是java虚拟机提供的轻量级同步机制 ​ 作用是: 1.保证可见性 2.禁止指令重排 3.不保证原子性 本篇具体就讲解 什么叫保证了可见性, 什么叫禁止指令重排,什么是原子性 而在这之前需要对JMM 有所了解 二,什么是JMM ​ JMM(java 内存模型 Java Memory Model 简称JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的

clinginh的博客 9724

2025机械考研复试面试问题汇总篇(含13门科目),考研机械复试专业面试常见重点问题总结!考研机械复试专业面试准备看这一篇就够了!

(完整版内容:)1.汇集了本科各科的重点知识及多个机械名校800多个常见问题,共8.7万字185页,涵盖机械所以专业课的重点知识和常见问答。 2.涉及机械大类重点科目,包括:材料力学、控制工程、数控技术、机械制造技术基础、理论力学、液压传动、机械设计、机械原理、工程材料、互换性与技术测量、测试技术、汽车理论、程序设计与C语言等13门必修课。

weixin_56510835的博客 1861

浅析volatile关键字

volatile 关键字声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)const 可以是 volatile(如只读的状态寄存器)指针可以是 volatile。

螺蛳粉只吃炸蛋的走风的博客 1052

YOLOv11涨点改进 | NeurIPS2025最佳论文!注意力改进篇| 引入Gated Attention门控注意力模块、太牛了!直接拯救了大模型的发展,助力目标检测、图像分割、图像分类有效涨点

本文提出了一种基于门控注意力机制(GatedAttention)的YOLOv11改进方法。该模块通过在标准注意力机制中引入头部特定的sigmoid门控,有效提升了模型性能和训练稳定性。实验表明,GatedAttention能够增强模型对关键信息的聚焦能力,减少无关区域关注,提高检测精度。同时,门控机制带来的非线性和稀疏性优化了计算效率,并缓解了训练不稳定性问题。文章详细介绍了模块原理、实现代码及三种改进方案配置方法,为YOLOv11在复杂场景下的目标检测提供了有效解决方案。

Ai缝合怪、助力小伙伴高效发论文 494

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

什么时候使用volatile关键字?

想必大家平时都见过volatile关键字,可是你知道什么时候需要使用volatile关键字吗? 直接看下面代码: int a = 100; while (a == 100) { // code } 这段程序编译时,如果编译器发现程序始终没有企图改变a的值,那它可能就会优化这段代码,变成while(true)的死循环使得程序执行的更快,然而编译器有时候也会做过度优化,它有时候可能没有意识到程序会改变a的值,却做了这种优化导致程序没有产生预期的行为。 这里为了产生预期的行为,需要阻止编译器做这种优化

qq_29426201的博客 2406

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

volatile关键字的作用

在我们了解volatile关键字之前,首先看一下代码; public class Mythread implements Runnable{ private boolean flag = false; @Override public void run() { try { //当前线程让出资源 sleep(20...

qq_37031675的博客 5060

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

14. Java中 volatile 关键字

volatile 关键字概念介绍,从整体层面了解 volatile 关键字;volatile 关键字与 synchronized 关键字的区别,这是本节的重点内容之一,了解 volatile 关键字与 synchronized 关键字的区别,才能更好地区分并掌握两钟关键字的使用;volatile 关键字原理介绍,也是本节课程的重点之一;volatile 关键字的使用,是本节课程的核心内容,所有的知识点都是围绕这一目的进行讲解的。概念。

这是一个web全栈开发的博客,取其精华去其糟粕,争取让大家一看就懂,一学就会,一用就成~ 1278

Java中volatile关键字详解

本文深入解析了Java中volatile关键字的核心机制和实际应用。文章首先介绍了volatile的两大主要特性——保证线程间共享变量的可见性和禁止指令重排序,从而确保多线程环境下数据操作的顺序性。随后,通过具体代码案例展示了volatile在控制线程停止、单例模式中防止指令重排序以及其在复合操作中可能出现的问题

欢迎来到我的技术博客 1620
上一篇: aspose将word转pdf时乱码,或者出现小方框问题
下一篇: docker安装ElasticSearch & Kibana
_Romeo
_Romeo 优质创作者: 后端开发技术领域 优质创作者: 后端开发技术领域
博客等级 码龄8年 2万+粉丝 104原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Romeo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值