Java-对 volatile 的理解
当一个变量被定义为 volatile
之后,它将具备两种特性:
当一个变量被定义为 volatile
之后,它将具备两种特性:
- 保证此变量对所有线程的可见性,但是不能保证原子性,如果满足以下规则则可以考虑使用
volatile
变量
- 运算结果不依赖变量的当前值或能够保证只有单一的线程修改变量的值
1 | /** |
1 | /** |
- 变量不需要与其他的状态变量共同参与不变约束
1 | /** |
并发关键字volatile(重排序和内存屏障)
漫画:什么是 volatile 关键字?
Java 并发编程实战 3.1.4
深入理解Java虚拟机(第2版) 12.3.3
- 禁止指令重排序优化
对内存屏障的理解:Store Barrier
Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。
Load Barrier
Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令。这使得从其它CPU暴露出来的程序状态对该CPU可见,这之后CPU可以进行后续处理。一个好例子是上面的BatchEventProcessor的sequence对象是放在屏障后被生产者或消费者使用。
Java 内存模型中 volatile 变量在写操作之后会插入一个 store 屏障,在读操作之前会插入一个 load 屏障。一个类的final字段会在初始化后插入一个 store 屏障,来确保 final 字段在构造函数初始化完成并可被使用时可见。
1 | class Example { |