一文读懂 volatile 关键字

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 一文读懂 volatile 关键字

  点击上方 **好好学java **,选择 **星标 **公众号


重磅资讯、干货,第一时间送达
今日推荐:有了这 4 款工具,老板再也不怕我写烂SQL了个人原创+1博客:点击前往,查看更多

作者:对弈
来源:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.html

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

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

    可见性

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

验证可见性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://sihai.blog.csdn.net/article/details/109465315

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 一文读懂 volatile 关键字


 上一篇
有了这 4 款工具,老板再也不怕我写烂SQL了 有了这 4 款工具,老板再也不怕我写烂SQL了
 点击上方 **好好学java **,选择 **星标 **公众号 重磅资讯、干货,第一时间送达 今日推荐:后端程序员必备:书写高质量SQL的30条建议个人原创+1博客:点击前往,查看更多 作者 | 爱码士人员 来源 | https://
2021-04-04
下一篇 
面试题——一条 sql 语句是如何经过 MySQL 的体系结构的? 面试题——一条 sql 语句是如何经过 MySQL 的体系结构的?
  点击上方 **好好学java **,选择 **星标 **公众号 重磅资讯、干货,第一时间送达 今日推荐:有了这 4 款工具,老板再也不怕我写烂SQL了个人原创+1博客:点击前往,查看更多 作者:李福春 来源:https://www
2021-04-04