volitate long JAVA_Java中volatile如何保证long和double的原子性操作

关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对于多个线程访问同一个实例变量需要加锁进行同步。

1 packageorg.fool.java.concurrent.volatiletest;2

3 importjava.util.concurrent.ExecutorService;4 importjava.util.concurrent.Executors;5

6 public classVolatileTest1 {7

8 private static volatile int count = 0;9

10 private static voidaddCount() {11 for (int i = 0; i < 100; i++) {12 count++;13 }14

15 System.out.println(Thread.currentThread().getName() + " count = " +count);16 }17

18 public static voidmain(String[] args) {19 ExecutorService executor =Executors.newCachedThreadPool();20

21 for (int i = 0; i < 100; i++) {22 executor.execute(newRunnable() {23 @Override24 public voidrun() {25 VolatileTest1.addCount();26 }27 });28 }29

30 executor.shutdown();31 }32 }

Note:

addCount()方法没有加synchronized

45ac780b99c32dd762207d481455f7e7.png

Console Output

22e63230ce3ab791b296f5cf23ccf810.png

预期结果应该是10000,尽管count被volatile修饰,保证了可见性,但是count++并不是一个原子性操作,它被拆分为load、use、assign三步,而这三步在多线程环境中,use和assgin是多次出现的,但这操作是非原子性的,也就是在read和load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公有内存中的变量不同步,所以计算出来的值和预期不一样,就产生了线程安全的问题,所以需要用synchronized进行加锁同步

addCount()方法用synchronized进行加锁同步

40871f73eb30145de0e8acde1efd7862.png

Console Output

3518e77c789583fa2926bd804bb77596.png

结果10000与预期一致

所以volatile只能保证可见性不能保证原子性,但用volatile修饰long和double可以保证其操作原子性。

bfaa2ad82e40d70be5ee0bb72b3e4a53.png

所以从Oracle Java Spec里面可以看到:

对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。

如果使用volatile修饰long和double,那么其读写都是原子操作

对于64位的引用地址的读写,都是原子操作

在实现JVM时,可以自由选择是否把读写long和double作为原子操作

推荐JVM实现为原子操作

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值