Volatile关键字解析

2020-04-01 16:11:57来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

Volatile关键字解析

volatile是Java虚拟机提供的轻量级的同步机制(“乞丐版”的synchronized)

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

可见性

指当多个线程访问同一个变量时,如果其中一个线程修改了这个变量的值,其他线程能够立即看得到修改的值

验证可见性demo:

import java.util.concurrent.TimeUnit;

class MyData {
    volatile int number = 0;
    public void addTo60() {
        number = 60;
    }
}
public class VolatileDemo {
    public static void main() {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName() + "\t updated number: " + myData.number);
        }, "AAA").start();
        while (myData.number == 0) {}
        System.out.println(Thread.currentThread().getName() + "\t mission is over");
    }
}

结果:

AAA  come in
main     mission is over
AAA  updated number: 60

不保证原子性

原子性:程序中的所有操作是不可中断的,要么全部执行成功要么全部执行失败

不保证原子性正是volatile轻量级的体现,多个线程对volatile修饰的变量进行操作时,会出现容易出现写覆盖的情况(i++)

验证不保证原子性demo:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData {
    volatile int number = 0;
    public void addPlusPlus() {
        number++;
    }
}
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlusPlus();
                }
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
    }
}

结果:

main    finally number value: 19109

解决不保证原子性问题:Atomic

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData {
    volatile int number = 0;
    public void addPlusPlus() {
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();
    public void addAtmic() {
        atomicInteger.getAndIncrement();
    }
}
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addAtmic();
                }
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger type,finally number  value: "
                + myData.atomicInteger);
    }
}

结果:

main     finally number value: 19746
main     AtomicInteger type,finally number  value: 20000

禁止指令重排

指令重排:为了提高程序运行效率,编译器可能会对输入指令进行重新排序,即程序中各个语句的执行先后顺序同代码中的顺序不一定一致。(但是它会保证单线程程序最终执行结果和代码顺序执行的结果是一致的,它忽略了数据的依赖性

源代码 -> 编译器优化重排 -> 指令并行重排 -> 内存系统重排 -> 最终执行指令

volatile能够实现禁止指令重排的底层原理:

  • 内存屏障(Memory Barrier):它是一个CPU指令。由于编译器和CPU都能够执行指令重排,如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,任何指令都不能和该条Memory Barrier指令进行重排序,即通过插入内存屏障指令能够禁止在内存屏障前后的指令执行重排序
    优化
  • 内存屏障的另外一个作用是强制刷新各种CPU的缓存数据,因此任何CPU上的线程都能够读取到这些数据的最新版本。以上两点正好对应了volatile关键字的禁止指令重排序和内存可见性的特点
  • 对volatile变量进行写操作时,会在写操作之后加入一条store屏障指令,将工作内存中的共享变量copy刷新回主内存中;对volatile变量进行读操作时,会在读操作之前加入一条load的屏障指令,从主内存中读取共享变量

应用场景:

  • 高并发环境下DCL单例模式使用volatile

    public class SingletonDemo {
        private static volatile SingletonDemo instance = null;
        private SingletonDemo() {
            System.out.println(Thread.currentThread().getName() + "我是构造方法SingletonDemo()");
        }
        public static SingletonDemo getInstance() {
            if (instance == null) {
                synchronized (SingletonDemo.class) {
                    if (instance == null) {
                        instance = new SingletonDemo();
                    }
                }
            }
            return instance;
        }
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    SingletonDemo.getInstance();
                }, String.valueOf(i)).start();
            }
        }
    }
    
  • JUC包下AtomicXxx类:原子类AtomicXxx中都有一个成员变量value,该value变量被声明为volatile,保证
    AtomicXxx类的内存可见性,而原子性由CAS算法&Unsafe类保证,结合这两点才能让AtomicXxx类很好地替代synchronized关键字。

    public class AtomicInteger extends Number implements java.io.Serializable {
        // ...
        private volatile int value;
        // ...
    }
    

原文链接:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:java String

下一篇:OpenWrite 整合七牛云图床!图片从此自我管控,不怕丢失!