java:volatile关键字的作用

1、为什么要使用volatile

在java的高并发编程中,每一个线程都会为其开辟其独自的本地内存,不对其它线程共享。该线程的本地内存放有从主内存读取的变量,后续该线程对变量的操作就只会在该线程的本地内存进行,不会对主内存的变量造成影响。

那就会造成一个问题,假设有a和b两个线程,线程a对一个变量c进行操作后,变量c发生了改变,但是在线程b的视角中,变量c并没有发生改变。

对于一个公共变量c,在线程a和b中,就有了两个不同的版本,造成a和b之间信息不同步,在后续要使用变量c的一系列过程中,就可能会出现不可预测的错误。

这时,我们需要让线程a对变量c的操作,对线程b可见,volatile就可以达到这个作用。

2、volatile

volatile是一个关键字,可以加在类的普通变量和静态变量上。可以保障可见性和有序性。

2.1 语义

  1. 在读volatile变量的时候,jmm会将本地内存对应的变量设置为无效,然后从主内存中读取。
  2. 在写volatile该变量的时候,jmm会将本地内存中对应的变量立即刷新回主内存中。

2.2 内存屏障

内存屏障是类同步屏障指令,是cpu或编译器在对内存随机访问的操作中的一个同步点,让此点之前的所有读写操作都执行后才可以执行此点之后的操作,避免代码重排序。

内存屏障其实就是一种jvm指令,java内存模型的重排规则会要求java编译器在生成jvm指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了java内存模型中的可见性和有序性。

内存屏障之前的所有写操作都要回写到主内存中。

内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果。

可以分为四大屏障:

  1. Load1;LoadLoad;Load2:Load1的读操作在Load2及后续读操作之前执行

  2. Store1;StoreStore;Store2:Store2及其后续写操作之前,操作1的写操作已经刷新回主内存中

  3. Load1;LoadStore;Store2:Store2及其后续写操作之前,保证Load1的读操作已经完成

  4. Store1;StoreLoad;Load2:保证Store1的写操作已经刷新回主内存中,Load2及后的读操作才能执行

插入屏障:

  1. volatile读 -> LoadLoad -> LoadStore -> 普通写/读操作
  2. 普通写/读操作 -> StoreStore屏障 -> volatile写 -> StoreLoad

3、volatile的使用细节

java 复制代码
public class Day0412 {

    static int num = 0;

    static volatile int numVolatile = 0;

    static volatile int numVolatileSynchronized = 0;

    static synchronized void addVolatileSynchronized() {
        numVolatileSynchronized++;
    }

    public static void main(String[] args) {

        System.out.println("---------------------------test01------------------");
        test01();

        while (Thread.activeCount() > 2) {
            try {Thread.sleep(1000);} catch (InterruptedException e) {}
        }

        num = 0;
        numVolatile = 0;
        numVolatileSynchronized = 0;

        System.out.println("---------------------------test02------------------");
        test02();

    }

    static void test01() {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    num++;
                }
            }).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    numVolatile++;
                }
            }).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    addVolatileSynchronized();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            try {Thread.sleep(1000);} catch (InterruptedException e) {}
        }

        System.out.println("num;"+num);
        System.out.println("numVolatile;"+numVolatile);
        System.out.println("numVolatileSynchronized;"+numVolatileSynchronized);

    }

    static void test02() {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    num++;
                    numVolatile++;
                    addVolatileSynchronized();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            try {Thread.sleep(1000);} catch (InterruptedException e) {}
        }

        System.out.println("num;"+num);
        System.out.println("numVolatile;"+numVolatile);
        System.out.println("numVolatileSynchronized;"+numVolatileSynchronized);

    }

}

由num、numVolatile和numVolatileSynchronized的数值对比,可以的得出volatile关键字不会保证原子性,在通过test01和test02的对比,可见volatile的内存屏障是真实生效的。

相关推荐
Rain的Java大神之路4 分钟前
百万并发直播弹幕系统设计全解:看懂了扇出,你就看懂了高并发的八成
java·后端·面试
\光辉岁月/20 分钟前
2.异常类
java·开发语言·异常类
淡海水21 分钟前
05-03-源码-Boehm GC分配与碎片管理
开发语言·算法·unity·c#·游戏引擎·gc
努力实干24 分钟前
C++ 面向对象与工程架构
开发语言·c++·架构
十年Java程序媛30 分钟前
JVM 基础与 GC 垃圾回收详解|可达性分析、分代模型、垃圾收集器、STW
java·开发语言·jvm·spring boot·后端·面试
估值探索者31 分钟前
【Python量化选股流水线 #03】候选股怎么排序?动量+低波+流动性三因子打分取 Top-N
开发语言·python·接口·api接口·股票数据接口·数据接口·股票数据api接口
骑士雄师32 分钟前
supervisor 中关于上下文多轮信息维护常用到的python语法
开发语言·python
曹牧41 分钟前
C#:字符串占位符与值数量不一致
开发语言·数据库·c#
2301_322414280443 分钟前
基于SSM框架的在线考试管理系统的设计与实现14750-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·redis·mysql·tomcat·maven·mybatis·ssm
clz13145211 小时前
RuoYi-Cloud 新建微服务并实现服务间调用(OpenFeign 实战)
java·ruoyi