Java并发:volatile的实现原理

synchronized 是一个重量级的锁, volatile 一般被比喻成轻量级的 synchronizedc++

volatile 是一个变量修饰符,只能用来修饰变量。数据库

volatile写:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。缓存

volatile读:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。.net

volatile实现原理
1)JMM把内存屏障指令分为下列四类:线程

StoreLoad Barriers是一个“全能型”的屏障,它同时具备其余三个屏障的效果。现代的多处理器大都支持该屏障(其余类型的屏障不必定被全部处理器支持)。执行该屏障开销会很昂贵,由于当前处理器一般要把写缓冲区中的数据所有刷新到内存中(buffer fully flush)。blog

Store:数据对其余处理器可见(即:刷新到内存)排序

Load:让缓存中的数据失效,从新从主内存加载数据 内存

2)JMM针对编译器制定的volatile重排序规则表编译器

是否能重排序    第二个操做
第一个操做    普通读/写    volatile读    volatile写
普通读/写              NO
volatile读    NO    NO    NO
volatile写         NO    NO
举例来讲,第三行最后一个单元格的意思是:在程序顺序中,当第一个操做为普通变量的读或写时,若是第二个操做为volatile写,则编译器不能重排序这两个操做。编译

从上表咱们能够看出:

当第二个操做是volatile写时,无论第一个操做是什么,都不能重排序。这个规则确保volatile写以前的操做不会被编译器重排序到volatile写以后。
当第一个操做是volatile读时,无论第二个操做是什么,都不能重排序。这个规则确保volatile读以后的操做不会被编译器重排序到volatile读以前。
当第一个操做是volatile写,第二个操做是volatile读时,不能重排序。
JMM内存屏障插入策略(编译器能够根据具体状况省略没必要要的屏障):

在每一个volatile写操做的前面插入一个StoreStore屏障。
对于这样的语句Store1;  StoreStore ; Store2,在Store2及后续写入操做执行前,保证Store1的写入操做对其它处理器可见。
在每一个volatile写操做的后面插入一个StoreLoad屏障。
对于这样的语句Store1;  StoreLoad ; Load2,在Load2及后续全部读取操做执行前,保证Store1的写入对全部处理器可见。
在每一个volatile读操做的后面插入一个LoadLoad屏障。
对于这样的语句Load1;  LoadLoad ; Load2,在Load2及后续读取操做要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
在每一个volatile读操做的后面插入一个LoadStore屏障。
对于这样的语句Load1;  LoadStore ; Store2,在Store2及后续写入操做被刷出前,保证Load1要读取的数据被读取完毕。
volatile保证可见性

volatile修饰的变量写以后将本地内存刷新到主内存,保证了可见性

volatile保证有序性

volatile变量读写先后插入内存屏障以免重排序,保证了有序性

volatile不保证原子性

volatile不是锁,与原子性无关

要我说,因为CPU按照时间片来进行线程调度的,只要是包含多个步骤的操做的执行,自然就是没法保证原子性的。由于这种线程执行,又不像数据库同样能够回滚。若是一个线程要执行的步骤有5步,执行完3步就失去了CPU了,失去后就可能不再会被调度,这怎么可能保证原子性呢。

为何 synchronized 能够保证原子性 ,由于被 synchronized 修饰的代码片断,在进入以前加了锁,只要他没执行完,其余线程是没法得到锁执行这段代码片断的,就能够保证他内部的代码能够所有被执行。进而保证原子性。(摘自http://www.hollischuang.com/archives/2673)

volatile不保证原子性的例子:

 /**
 * 建立10个线程,而后分别执行1000次i++操做。目的是程序输出结果10000
 * 可是,屡次执行的结果都小于10000。这其实就是volatile没法知足原子性的缘由。
 */
public class Test {
    public volatile int inc = 0;

    public void increase() {
        inc++;
    }

    public static void main(String[] args) {
        final Test test = new Test();
        for (int i = 0; i < 10; i++) {
            new Thread() {
                public void run() {
                    for (int j = 0; j < 1000; j++)
                        test.increase();
                };
            }.start();
        }

        while (Thread.activeCount() > 1)             // 保证前面的线程都执行完             Thread.yield();         System.out.println(test.inc);     } } ---------------------  做者:qq_43171869  来源:CSDN  原文:https://blog.csdn.net/qq_43171869/article/details/83660440  版权声明:本文为博主原创文章,转载请附上博文连接!

相关文章
相关标签/搜索