Volatile

                                                    **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
LoadLoadBarrier

volatile 读操作

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 变量提供理想的线程安全,必须同时满足下面两个条件:

  1. 对变量的写操作不依赖于当前值。
  2. 该变量没有包含在具有其他变量的不变式中。

3.性能考虑:使用 volatile 变量的主要原因是其简易性:在某些情形下,使用 volatile 变量要比使用相应的锁简单得多。使用 volatile 变量次要原因是其性能:某些情况下,volatile 变量同步机制的性能要优于锁。
volatile 操作不会像锁一样造成阻塞,因此,在能够安全使用 volatile 的情况下,volatile 可以提供一些优于锁的可伸缩特性。如果读操作的次数要远远超过写操作,与锁相比,volatile 变量通常能够减少同步的性能开销。

六丶end

	与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。
	如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值—— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。
	然而使用 volatile 的代码往往比使用锁的代码更加容易出错。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值