• 3.JUC之volatile


    原文链接:http://blog.csdn.net/zteny/article/details/54888629

    一、简介

    volatile是Java语言的关键字,用来修饰可变变量(即该变量不能被final修饰),且必须是至少类内可见。所以它是可以修饰带static的变量。这我自己下定义。

    它是被设计用来修饰被不同线程访问和修改的变量。来自 百度百科

    二、功能

    volatile提供了一个高效的同步机制,她在某些情况下可以代替synchronized实现更轻量和高效的同步机制,同时也更为脆弱,更难于掌控。被volatile修饰的变量具有内存可见性,但不具有原子性。至于什么是可见性,前面已经做过简单介绍,接下来我们进一步来看什么是可见性。

    1. 内存可见性

    首先为什么会出现内存可见性问题呢? 
    想完全理解这个问题,请自行阅读《深入理解计算机系统》吧!这里简单说一下,

    每个线程都有它自己的线程上下文,包括栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的共享该进程的整个虚拟地址空间。——来自《深入理解计算机系统》

    下面这个说法可能并不严谨,甚至是有误,但对我们理解这个问题有帮忙。 
    如你所知,所有计算都发生CPU,然而它直接操作主存的效果比较远,不如CPU的缓存区,更远不如寄存器。其次,如上面所有的系统会为每个线程分配自己的线程上下文。在这两个大提前下,可能简化的理解为线程有自己的高速cache,即所有线程操作变量时,都不会直接操作主存。当发生cache miss时,从主存拷贝到cache,这些都是你懂的啦。跟所有的cache一样,都存在一致性的问题。

    即是正常情况下什么时候发生cache冲刷回主存并不可控。 
    不正常情况下,退出临界区时即刻强制更新主存。另一种情况,即我们要讨论的volatile。被volatile修饰的变量比较特殊,表示直接操作主存,不需要通过cache。直接要用时直接从主存取(注意取出来还是会把值放在自己的上下文,这点后面需要用到),用完写直接回主存。这就是内存可见性

    2. 可不完全替代synchronized

    之前整理synchronized的时候忘了讲synchronized怎么实现同步的,在这里顺便带出来吧。 
    synchronized是通过临界区实现同步的,临界区的同步方式是同一个时间只有最多一个线程进入临界区,也就是说只能保证原临界区具有原子性。这是什么意思呢,先来看一下面例子吧。

    
    void barfoo() {
        new Thread(() -> {
                for(int v=0; v<100; v++) bar();
            }).start();
    
            new Thread(() -> {
                for(int v=0; v<100; v++) foo();
            }).start();
        }
    }
    
    int v = 0;
    void bar() {
        final int t = v + 1;
        v++;
        try {
            TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10));
        } catch (InterruptedException e) { }
        if(t != v)System.out.println("not match");
    }
    
    synchronized void foo() {
        final int t = v + 1;
        v++;
        try {
            TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10));
        } catch (InterruptedException e) { }
        if(t != v)System.out.println("not match");
    }

    执行barfoo()的结果打印了not match。 
    synchronized只是通过线程在离开临界区时会把线程上下文冲刷回主存,从而实现一致性,但对于变量v而言不具备原子性,更无法保证能够一致性。

    volatile可部分替代synchronized,也就是说在特定条件或者场景下可以替代synchronized。上面我们提到过volatile具有内存可见性,但不具有原子性,而synchronized实际是上能够实现原子性的。这一点是volatile做不到的,也是这种场景下volatile无法代替synchronized。 
    这一点就不举例了,主要知道什么是原子性和非原子性即可自行实验了。如:a += b就一个非原子性操作。

    三、总结

    1. 简单的了解了volatile的用法;
    2. 进一步了解内存可见性和synchronized实现原理;
    3. volatile与synchronized的差异,以及可代替场景;
    4. volatile通过内存可见性实现同步,即线程A操作了被volatile修饰的变量之后,线程B立马可能读到线程A的修改结果。

    我的另外一篇文章也有介绍:http://www.cnblogs.com/xuzekun/p/7426713.html

  • 相关阅读:
    MySQL too many connections
    【MySQL】 清除等待连接
    wmic 获得系统硬件信息
    Linux 修改用户名
    初步了解虚拟化
    MySQL show 语句
    php去除bom
    jq闭包
    git
    地址收藏
  • 原文地址:https://www.cnblogs.com/xuzekun/p/7481872.html
Copyright © 2020-2023  润新知