volatile

因为一直没使用过,故而没有理解 volatile 的含义。近来好好研究了下。

volatile 是与同步有关的关键字,可以实现轻量级的同步。好处是使用简单,直接修饰变量或者函数即可,缺点是无原子性,无法实现计数器等。

编译器或者 JVM 会做一个优化:在寄存器或线程空间内,保存某个变量的副本,即某个变量可能在多处存在,那么,每个线程看到的值,有可能是不同的。清楚了这一点,就不难理解 volatile 存在的含义:提示编译器或者 JVM,该变量不能被保存为多份,即,该值有可能被其它线程并发更新,所以,每次读取的时候,都必须去内存中读取,同时,如果有更新,也必须同步更新原始位置。

当然,同步的问题解决了,带来的负面影响就是,性能会有影响。但是,相对于加锁,其对性能的影响应该较低。说应该,也是因为编译器或者 JVM 对锁优化的缘故,无法保证每次测试的结果都一致。

更重要的问题,是要理解该关键字的使用场景。《Java 理论与实践:正确使用 volatile 变量》一文总结了两点,借用下:

1. 对变量的写操作不依赖于当前值

所谓“依赖当前值”,比如说 i++,这就是要依赖当前值,要取出当前值后才能加 1 。为什么不能依赖当前值呢?因为 volatile 无原子性。我们都知道,对于 i++ 这一个操作,其实包含了读取、修改、写入三步操作。假如多个线程都要执行增量操作,即使有 volatile 关键字修饰,其修改后的值也有可能丢失。

2. 该变量没有包含在具有其它变量的不变式中

对于文字表述,咱更喜欢代码来得实在:

public void testClass {

    private int upper;

    private int lower;

    public void setUpper(int upper) {

        if (upper < lower) {

            //error

        }

        this.upper = upper;

    }

    public void setLower(int lower) {

        if (lower > upper) {

            // error

        }

        this.lower = lower;

    }

}

如果原始值是(0,10),两个线程,同一时间调用 setUpper(5) 和 setLower(6),有可能最后的结果是(5, 6)。即使使用 volatile 修饰,结果也一样。这当然不是我们希望的结果。

这时就很好理解第 2 点了,即这两个变量的修改,都依赖于对方。其实,这两点可以汇总成一句话:变量真正独立于其它变量和自己以前的值时,才能使用 volatile。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值