本文主要为摘抄拼凑
Java中volatile的作用:1、Java提供了volatile关键字来保证可见性;2、保证有序性,代码为【context = loadContext();inited = true;】;3、提供double check。(摘抄)
如何理解:
Java编程语言允许线程访问共享变量,那么为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量,或者把这个变量声明成volatile,可以理解volatile是轻量级的synchronized。
使用volatile可以在Java线程内存模型确保所有线程看到这个变量的值是一致的,在多个处理器中保证了共享变量的“可见性”。
volatile两核心三性质?
两大核心:JMM内存模型(主内存和工作内存)以及happens-before
三条性质:原子性,可见性,有序性
底层实现:
volatile的底层实现是通过插入内存屏障,但是对于编译器来说,发现一个最优布置来最小化插入内存屏障的总数几乎是不可能的,所以,JMM采用了保守策略。如下:
在每一个volatile读操作后面插入一个LoadLoad屏障,用来禁止处理器把上面的volatile读与后面任意操作重排序 在每一个volatile写操作前面插入一个StoreStore屏障,用来禁止volatile写与前面任意操作重排序 在每一个volatile写操作后面插入一个StoreLoad屏障,用来禁止volatile写与后面可能有的volatile读/写操作重排序 在每一个volatile读操作前面插入一个LoadStore屏障,用来禁止volatile写与后面可能有的volatile读/写操作重排序。
如何正确使用volatile变量
在某些情况下,如果读操作远远大于写操作,volatile 变量可以提供优于锁的性能优势。
可是volatile变量不是说用就能用的,它必须满足两个约束条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中。
第一个条件的限制使volatile变量不能用作线程安全计数器。虽然 i++ 看上去类似一个单独操作,实际上它是一个读取-修改-写入三个步骤的组合操作,必须以原子方式执行,而 volatile不能保证这种情况下的原子操作。正确的操作需要使i的值在操作期间保持不变,而volatile 变量无法做到这一点。
volatile和synchronized区别
- volatile比synchronized执行成本更低,因为它不会引起线程上下文的切换和调度
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile只能用来修饰变量,而synchronized可以用来修饰变量、方法、和类。
- volatile可以实现变量的可见性,禁止重排序和单次读/写的原子性;而synchronized则可以变量的可见性,禁止重排序和原子性。
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
volatile和atomic原子类区别
- Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前
- 但是Volatile对复合操作不能保证原子性。例如用volatile修饰i变量,那么i++操作就不是原子性的。
- atomic原子类提供的atomic方法可以让i++这种操作具有原子性,如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作,但是atomic原子类一次只能操作一个共享变量,不能同时操作多个共享变量。
总结
总结一下volatile的特性``
- volatile可见性;对一个volatile的读,总可以看到对这个变量最终的写volatile有序性;JVM底层采用“内存屏障”来实现volatile语义volatile原子性;volatile对单个读/写具有原子性(32位Long、Double),但是复合操作除外,例如i++