• Java中volatile如何保证long和double的原子性操作


    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11426473.html

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

     1 package org.fool.java.concurrent.volatiletest;  
     2   
     3 import java.util.concurrent.ExecutorService;  
     4 import java.util.concurrent.Executors;  
     5   
     6 public class VolatileTest1 {  
     7   
     8     private static volatile int count = 0;  
     9   
    10     private static void addCount() {  
    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 void main(String[] args) {  
    19         ExecutorService executor = Executors.newCachedThreadPool();  
    20   
    21         for (int i = 0; i < 100; i++) {  
    22             executor.execute(new Runnable() {  
    23                 @Override  
    24                 public void run() {  
    25                     VolatileTest1.addCount();  
    26                 }  
    27             });  
    28         }  
    29   
    30         executor.shutdown();  
    31     }  
    32 } 

    Note:

    addCount()方法没有加synchronized

    Console Output

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

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

    Console Output

    结果10000与预期一致 

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

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

    • 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。
    • 如果使用volatile修饰long和double,那么其读写都是原子操作
    • 对于64位的引用地址的读写,都是原子操作
    • 在实现JVM时,可以自由选择是否把读写long和double作为原子操作
    • 推荐JVM实现为原子操作 

    Reference

    http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

    http://www.cnblogs.com/louiswong/p/5951895.html

    http://ifeve.com/volatile/

    http://www.infoq.com/cn/articles/java-memory-model-4/

  • 相关阅读:
    如何规范自己的编程以及软件开发目录(二)
    关于README的内容
    关于编程编程规范以及开发目录的规范性
    第五章:条件、循环以及其他语句(上)
    第四章 当索引行不通时
    python-zipfile模块
    python-shutil模块
    python-sys模块
    python-os模块
    python-threading.Thread(多线程)
  • 原文地址:https://www.cnblogs.com/agilestyle/p/11426473.html
Copyright © 2020-2023  润新知