java中volatile关键字

简介: java中volatile关键字

volatile

什么是volatile

volatile是java虚拟机提供的轻量级同步机制

作用

1.可见性: 当一个线程修改了volatile修饰的变量的值,其他线程可以立即看到这个修改,保证了共享变量的可见性。

2.禁止指令重排序: 编译器和处理器在编译和执行代码时,可能会对指令进行重排序,但是volatile关键字可以禁止这种重排序,保证了程序的正确性。

3.保证原子性: volatile关键字可以保证一些简单的操作的原子性,例如++操作,但是对于复合操作,volatile关键字无法保证原子性。

底层实现

“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

  lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;

  2)它会强制将对缓存的修改操作立即写入主存;

  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

Volatile原理

Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

  在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

image.png

当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。

  而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

当一个变量定义为 volatile 之后,将具备两种特性

1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

  2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

volatile 性能

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行

面试中可能遇到的问题

单例模式中volatile的作用:(DCL(Double Check Lock)单例为什么要加Volatile?)

防止代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化。

volatile防止指令重排,在DCL中,防止高并发情况下,指令重排造成的线程安全问题。 下面是DCL的代码过程:

class Singleton{
    private volatile static Singleton instance = null;   //禁止指令重排
    private Singleton() {
    }
    public static Singleton getInstance() {
        if(instance==null) { //减少加锁的损耗
            synchronized (Singleton.class) {
                if(instance==null) //确认是否初始化完成
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

volatile关键字和synchronized关键字的区别

  1. volatile关键字保证了共享变量的可见性和禁止指令重排序,但是无法保证原子性,而synchronized关键字可以保证原子性、有序性和可见性。
  2. volatile关键字适用于一些简单的操作,例如++操作,而synchronized关键字适用于复合操作。
  3. volatile关键字不会造成线程阻塞,而synchronized关键字可能会造成线程阻塞。

volatile能不能保证线程安全?

volatile不能保证线程安全,只能保证线程可见性,不能保证原子性

flag常量未使用volatile关键字的时候,程序一直不停止,因为无法感知flag的变化

使用volatile关键字后,因为volatile保证了可见性,感知到了flag的变化,程序停止并打印

image.png

相关文章
|
4月前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
71 0
|
2月前
|
SQL 缓存 安全
[Java]volatile关键字
本文介绍了Java中volatile关键字的原理与应用,涵盖JMM规范、并发编程的三大特性(可见性、原子性、有序性),并通过示例详细解析了volatile如何实现可见性和有序性,以及如何结合synchronized、Lock和AtomicInteger确保原子性,最后讨论了volatile在单例模式中的经典应用。
42 0
|
3月前
|
缓存 Java 编译器
JAVA并发编程volatile核心原理
volatile是轻量级的并发解决方案,volatile修饰的变量,在多线程并发读写场景下,可以保证变量的可见性和有序性,具体是如何实现可见性和有序性。以及volatile缺点是什么?
|
4月前
|
安全 Java 编译器
Java 中的 volatile 变量
【8月更文挑战第22天】
30 4
|
4月前
|
缓存 安全 Java
Java里为什么单利一定要加volatile呢?
【8月更文挑战第11天】Java里为什么单利一定要加volatile呢?
37 3
|
4月前
|
缓存 安全 Java
Java里volatile底层是如何实现的?
【8月更文挑战第11天】Java里的volatile底层是如何实现的?
29 2
|
5月前
|
存储 SQL Java
Java实现关键字模糊查询的高效方法及实践
实现关键字模糊查询的方法有多种,每种方法都有其适用场景。在选择合适的方法时,应考虑实际需求、数据量大小、性能要求等因素。正则表达式适用于处理简单文本或小数据集;数据库模糊查询适用于存储在RDBMS中的数据;而第三方库,则适合需要进行复杂搜索的大型项目。选用合适的工具,可以有效提升搜索功能的性能和用户体验。
102 6
|
4月前
|
安全 Java
|
5月前
|
算法 Java API
多线程线程池问题之synchronized关键字在Java中的使用方法和底层实现,如何解决
多线程线程池问题之synchronized关键字在Java中的使用方法和底层实现,如何解决
|
4月前
|
缓存 Java 编译器
Java中的volatile 变量是什么
【8月更文挑战第10天】Java中的volatile 变量是什么
52 0