**Volatile**
本章内容将介绍Volatile特性作用,原理,使用场景
一丶特性作用
1.内存可见性:A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道,使用volatile关键字,会让所有线程都会读到变量的修改值。
2.当线程A开始运行的时候,会把值从内存中读到A线程的工作区,在运行过程中直接copy,并不会每次都去读取堆内存,这样,当主线程修改值之后,A线程感知不到,所以不会停止运行.
3.使用volatile,将会强制所有线程都去堆内存中读取值.
4.volatile并不能保证多个线程共同修改变量时所带来的不一致问题,也就是说volatile不能替代synchronized
二丶volatile的实现细节
1. 字节码层面
ACC_VOLATILE

2. JVM层面
volatile内存区的读写 都加屏障
StoreStoreBarrier
volatile 写操作
StoreLoadBarrier
LoadLoadBarriervolatile 读操作
LoadStoreBarrier
3. OS和硬件层面
hsdis - HotSpot Dis Assembler
windows lock 指令实现 | MESI实现
三丶适用场景
1.状态标志:volatile 变量的规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机
volatile boolean shutdownRequested;
public void shutdown() {
shutdownRequested = true;
}
public void doWork() {
while (!shutdownRequested) {
// do stuff
}
}
2.一次性安全发布:在缺乏同步的情况下,可能会遇到某个对象引用的更新值(由另一个线程写入)和该对象状态的旧值同时存在。
1. 线程 1 进入 getInstance() 方法。
2. 由于 instance 为 null,线程 1 在 //1 处进入synchronized 块。
3. 线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非null。
4. 线程 1 被线程 2 预占。
5. 线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将instance 引用返回,返回一个构造完整但部分初始化了的Singleton 对象。
6. 线程 2 被线程 1 预占。
7. 线程 1 通过运行 Singleton 对象的构造函数并将引用返回给它,来完成对该对象的初始化。
public class Mgr06 {
private static volatile Mgr06 INSTANCE; //JIT
private Mgr06() {
}
public static Mgr06 getInstance() {
//第一次null检查
if (INSTANCE == null) {
//双重检查
synchronized (Mgr06.class) { //1
//第二次null检查
if (INSTANCE == null) { //2
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06(); //3
}
}
}
return INSTANCE;
}
}
3.开销较低的“读-写锁”策略:如果读操作远远超过写操作,您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。
使用 synchronized 确保增量操作是原子的,并使用 volatile 保证当前结果的可见性。如果更新不频繁的话,
该方法可实现更好的性能,因为读路径的开销仅仅涉及volatile 读操作,这通常要优于一个无竞争的锁获取的开销。
@ThreadSafe
public class Test {
@GuardedBy("this")
private volatile int value;
//读操作,没有synchronized,提高性能
public int getValue() {
return value;
}
//写操作,必须synchronized。因为x++不是原子操作
public synchronized int increment() {
return value++;
}
}
四丶正确使用 Volatile 变量
Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized ”;
与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是synchronized 的一部分。
1.Volatile 变量:Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。
2.正确使用 volatile 变量的条件:您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中。
3.性能考虑:使用 volatile 变量的主要原因是其简易性:在某些情形下,使用 volatile 变量要比使用相应的锁简单得多。使用 volatile 变量次要原因是其性能:某些情况下,volatile 变量同步机制的性能要优于锁。
volatile 操作不会像锁一样造成阻塞,因此,在能够安全使用 volatile 的情况下,volatile 可以提供一些优于锁的可伸缩特性。如果读操作的次数要远远超过写操作,与锁相比,volatile 变量通常能够减少同步的性能开销。
六丶end
与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。
如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值—— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。
然而使用 volatile 的代码往往比使用锁的代码更加容易出错。
654

被折叠的 条评论
为什么被折叠?



