java volatile用法_Java开发教程:volatile变量的使用建议及错误用法

volatile变量的使用建议

使用volatile变量的主要原因是其单个字段同步操作的简易性。如果只使用了volatile就能实现线程安全,就放心地使用它。否则如果在同时还需要添加其他的同步措施,那就不要使用。

正确使用的场景举例:变量本身标志是一种状态,在代码中需要确保这些状态的可见性,这时就可使用volatile。

volatile变量的使用仅仅是一个状态标志, 用于指示发生了一个重要的一次性事件, 例如完成初始化或请求终止。

778af6eb98a147294f75ab736c55d506.pngvolatile变量的使用建议及错误用法

这样只要任何一个线程调用了shutdown() , 其他线程在执行do Work时都可以立即感知到shutdown Requested变量的变化。这种类型的状态标记的一个公共特性是:通常只有一种状态转换,如标志从false转换为true。

这时使用volatile比synchronized要简单得多, 同时使用synchronized还会影响系统的吞吐量。

volatile变量的错误用法

注意:单个volatile变量单独的读、写操作具有原子性。但是对于类似于++, -, 逻辑非!这种复合操作,这些操作整体上不具有原子性。

f2be003599b4083b470b6434a61cedbf.pngvolatile变量的使用建议及错误用法

如下面例子:

f7d5dda2535677a96e49805ec6857816.pngvolatile变量的使用建议及错误用法

造成这种情况的原因是因为++操作分三个操作完成的。我们执行反编译命令java p-c

Volatile Test.class, increase() 函数中race++, 我们看到由6条字节码指令构成(return指令不是race++产生的, 不算在内) 。

740777c3abe9b0509e63b2ac368f19a0.pngvolatile变量的使用建议及错误用法

字节码释义如下:

a load_o将this引用推送至栈顶

dup复制栈顶值, 并将其压入栈顶, 即此时操作数栈上有连续相同的this引用;

get field弹出栈顶的对象引用, 获取其字段race的值并压入栈顶。第一次操作

iconst _ 1将int型(1) 推送至栈顶

iadd弹出栈顶两个元素相加(race+1) , 并将计算结果压入栈顶。第二次操作

put field从栈顶弹出两个变量(累加值, this引用) , 将值赋值到this实例字段race上。

第三次操作从字节码层面很容易分析出来并发失败的原因了,假如有两条线程同时执行这条语句,

(1) 线程A, 线程B同时执行get field指令把race的值取到操作栈顶时, volatile关键字可以保证来race的值在此时是正确(最新的值)的。

(2) 线程B依次执行完了后续操作i add和put field, 此时主内存中race的值已被增大1。

(3) 线程A操作栈顶的race值race值就变成了过期的数据。这时线程A执行i add、put field后就会把较小的值同步会主内存了。

所以volatile变量只能保证共享变量的可见性, 不能保证复合操作的原子性。在此种场景中, 我们仍然要通过加锁(synchronized或JUC包中的原子类) 来保证原子性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值