volatile 的内存语义

volatile 修饰的变量具有以下特性:

  • 可见性。对一个 volatile 变量的读,总是能看到任意线程对这个 volatile 变量最后的写入。
  • 原子性。对任意单个 volatile 变量的读/写具有原子性(除去 double 和 float 类型变量,因为这两种类型变量为 64 位类型)。

一个 volatile 变量的单个读/写操作,与锁对普通变量的读/写,它们之间的执行效果相同。

volatile 的 happens-before 规则:一个 volatile 的写 happens-before 于volatile 的读。

volatile 写–读建立的 happens-before 关系

	class ReorderExample {
	    int a = 0;
	    boolean flag = false;
	    
		public void writer() {
			a = 1;       // 1
			flag = true; // 2
		}
	
		public void reader() {
		    if(flag) {         // 3
		        int i = a * a; // 4
		    }
		}
	}

假设线程 A 执行 writer() 方法之后,线程 B 执行 reader() 方法。根据 happnes-before 规则进行分析:
1)根据程序次序规则,1 happens-before 2;3 happens-before 4。
2)根据 volatile 规则,2 happens-before 3。
3)根据 happens-before 的传递性规则,1 happens-before 4。

volatile 的内存语义

当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存。
当读一个 volatile 变量时,JMM 会把该线程对应的本地内存设置为无效。

volatile 的线程通信理解:
volatile 变量修改后,因为 volatile 的读总能看到 volatile 的写,在对 volatile 修饰变量进行写入操作时,本地内存中的共享变量进行修改后直接刷新到主内存;此时进行 volatile 读时,本地内存直接无效化,需要重新从主内存中获取。即 volatile 的一次写读就是线程之间进行一次通信,保证数据正确。

volatile 内存语义的实现

在每个 volatile 写操作的前面插入一个 StoreStore 屏障。 写操作执行前必须保证上面数据对其他处理器可见。
在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。写操作之后要保证数据刷新到主内存。
在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。保证读操作比后续装载操作先装载数据,防止重排序导致数据执行错误。
在每个 volatile 读操作的后面插入一个 LoadStore 屏障。保证读操作比后续存储操作先装载数据,使后续存储操作获得到最新数据。

总结

读 volatile 操作通过将其他线程的本地内存无效化,强制从主内存中获取数据,达到数据实时更新效果。通过该方式,volatile 的写与读操作相当于线程间进行隐式通信。

引申一点,锁的内存语义与 volatile 一致,释放锁后数据必须对获取锁可见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Volatile是一种Java中的关键字,用于标识变量是易变的,即该变量的值可能会在不同的线程中发生改变。Volatile底层原理涉及到Java内存模型。 Java内存模型定义了线程如何与内存交互以及线程之间如何共享内存Java内存模型将内存分为主内存和线程工作内存。主内存是所有线程共享的内存区域,而线程工作内存是每个线程独立拥有的内存区域。 当一个线程访问一个volatile变量时,它会从主内存中读取最新的值。而当一个线程更新一个volatile变量时,它会将新的值立即写入主内存中。这保证了所有线程对volatile变量的读写操作都是可见的。 此外,volatile还具有禁止指令重排序的作用。在多线程并发编程中,编译器为了提高程序执行效率可能会对指令顺序进行重排序,但是这种重排序可能会导致并发问题。使用volatile可以禁止编译器对volatile变量的指令进行重排序,保证了程序的正确性。 总之,volatile的底层原理是基于Java内存模型的,它保证了多线程环境下对volatile变量的可见性和禁止指令重排序的特性。 ### 回答2: VolatileJava中的关键字之一,用于修饰变量,主要用于多线程编程中,以保证线程间变量的可见性和顺序性。 Volatile的底层原理主要是通过内存屏障(Memory Barrier)和禁止重排序来实现的。内存屏障是一种CPU指令,能够强制刷新处理器缓存并保证读/写操作顺序的一致性。当一个线程修改了一个被volatile修饰的变量的值时,会立即将该值刷新到主内存,并通知其他线程对对应变量的缓存失效,强制其他线程从主内存重新读取最新值。 此外,volatile还可以禁止指令重排,保证代码的有序执行。在有volatile修饰的变量之前的指令一定会在其后的指令之前执行。这样可以避免了由于指令重排导致的数据不一致问题。 总之,Volatile底层原理主要通过内存屏障以及禁止指令重排来保证线程间变量的可见性和顺序性。它能够确保一个变量在多个线程之间的可见性,尤其用于一个线程修改了变量值时,其他线程能够立即感知到变量的变化,并从主内存中重新读取最新值,从而避免了线程间数据不一致的问题。同时,它还通过禁止指令重排,保证了代码的有序执行,避免了由于指令重排导致的逻辑错误。因此,在多线程编程中,合理使用Volatile关键字能够确保程序的正确性和稳定性。 ### 回答3: VolatileJava中的关键字,用于修饰变量。它的底层原理是通过禁止线程内部的缓存变量副本,直接访问主存中的变量值,保证了多线程环境中的可见性和有序性。下面详细解释其底层原理。 在多线程环境下,每个线程都有自己的工作内存(线程的私有内存),存放变量的副本。由于性能原因,线程在执行操作时,通常会先将变量从主存中读取到工作内存中进行操作,然后再将修改的结果写回主存。这种操作称为“读写操作的优化”。 当一个变量被volatile修饰时,它的读写操作会具有特殊的语义。当一个线程对volatile修饰的变量进行写操作时,它会首先将值写入工作内存,然后立即刷新到主存中,并且通知其他线程该变量的值已经被修改。而当一个线程对volatile变量进行读操作时,它会立即从主存中读取最新的值,并且在读之前使自己的工作内存失效,以保证读操作获取的是最新值。 这种特殊的语义使得volatile能够保证多线程环境下的可见性和有序性。通过禁止线程内部的缓存变量副本,保证了每个线程对volatile变量的读写操作都是基于主存中最新的值,从而避免了数据不一致的问题。同时,由于读操作会使工作内存失效,写操作会立即刷新到主存,保证了变量的修改对其他线程的可见性和顺序性。 总结起来,volatile的底层原理是通过禁止线程内部的变量副本,直接访问主存中的变量值,保证了在多线程环境下的可见性和有序性。它对于一些简单的变量操作可以替代锁,同时也可以用于线程间的通信,但并不能保证原子性。因此,在使用volatile时,需要根据具体的场景和需求来判断是否合适。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值