zoukankan      html  css  js  c++  java
  • 并发学习第五篇——volatile关键字

    先给出一个总体的结论

    volatile保证并发要解决的三大问题中的可见性和有序性

    另外,保证对单次读/写的原子性(复合运算不保证)

    前置知识

      JMM模型,见之前的一篇:https://www.cnblogs.com/yb38156/p/9427181.html

      happens-before 

    定义:
    Two actions can be ordered by a happens-before relationship.If one action happens before another, then the first is visible to and ordered before the second.
    规则:
    Each action in a thread happens before every subsequent action in that thread.
    • An unlock on a monitor happens before every subsequent lock on that monitor.
    • A write to a volatile field happens before every subsequent read of that volatile.
    • A call to start() on a thread happens before any actions in the started thread.
    • All actions in a thread happen before any other thread successfully returns from a join() on that thread.
    • If an action a happens before an action b, and b happens before an action c, then a happens before c.

    定义讲的是:

      如果a happen-before b,则a所做的任何操作对b是可见的(这里的before不是时间意义上的先后)

    规则讲的是:

    • 同一个线程中,前面的操作 happen-before 后续的操作(即单线程内按代码顺序执行。但是,在不影响单线程

       环境执行结果的前提下,编译器和处理器可以进行指令重排序,这是合法的。换句话说,这一规则无法保证编译重排和指令重排)

    • 监视器上的解锁操作 happen-before 其后续的加锁操作。(Synchronized 规则)
    • 对volatile变量的写操作 happen-before 后续的读操作。(volatile 规则)
    • 线程的start() 方法 happen-before 该线程所有的后续操作。(线程启动规则)
    • 线程所有的操作 happen-before 其他线程在该线程上调用 join 返回成功后的操作
    • 如果 a happen-before b,b happen-before c,则a happen-before c(传递性)

    运行原理

    保证可见性

    线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作

    操作主存中的变量时,每个线程会拷贝一个该变量的副本

    当volatile修饰的变量在一个线程中被修改后,会立即强制刷新到主存,并使得其他线程中读取的该变量的副本失效

    其他线程再次读取使用该变量时需要重新从主存中加载一份

    保证有序性

    体现在上面happens-before规则的第三条:对volatile变量的写操作 happen-before 后续的读操作

    JVM通过添加"内存屏障"保证volatile的可见性和有序性

    对volatile变量进行写操作时,前后分别加StoreStore和StoreLoad屏障

    对volatile变量进行读操作时,在读操作之后加LoadLoad和LoadStore屏障

    典型应用

    java.util.concurrent.atomic包下的各种AtomicBoolean,AtomicInteger中实际保存值的变量,均定义为volatile的

    例如AtomicBoolean:

    public class AtomicBoolean implements java.io.Serializable {
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicBoolean.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;
    
        public AtomicBoolean(boolean initialValue) {
            value = initialValue ? 1 : 0;
        }
        public final boolean compareAndSet(boolean expect, boolean update) {
            int e = expect ? 1 : 0;
            int u = update ? 1 : 0;
            return unsafe.compareAndSwapInt(this, valueOffset, e, u);
        }
        ...
    }

    在很多其他的同步类的源码中,例如ConcurrentHashMap等,也可以看到他的影子

    程序员单独在业务中使用,只在单例模式中看到过,不过自己写单例的情况,也早已经被Spring取代了

  • 相关阅读:
    Maven导包失败三种解决方案-Could not transfer artifact
    MySQL远程登录赋权操作各命令的意思
    大数据技术与应用课堂测试01
    软件体系架构课堂测试01
    设计模式复习笔记23
    设计模式复习笔记22
    设计模式复习笔记21
    设计模式复习笔记20
    设计模式复习笔记19
    设计模式复习笔记18
  • 原文地址:https://www.cnblogs.com/yb38156/p/14434479.html
Copyright © 2011-2022 走看看