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的内存屏障是真实生效的。

相关推荐
xiaoye-duck2 分钟前
《C++ 项目:手写大模型接入 SDK》LLMManager架构设计与实现
开发语言·网络·c++·语言模型
一条大祥脚25 分钟前
CuTe 与 CuTe DSL:从 C++ 模板元编程到 Python DSL 的 GPU 编程演进
开发语言·c++·python·cutlass·cute·cute dsl
学长毕设1 小时前
基于SpringBoot的野生动物公益保护平台设计与实现-附源码
java·spring boot·后端
FRing031 小时前
vue2干货总结8(路由模块封装、router-link、声明式导航、路由重定向、编程式导航)
开发语言·前端·vue.js·vue2
旖旎夜光1 小时前
【C++ 基础入门】C++ 多态(上):虚函数、重写、override/final 与抽象类
开发语言·c++·笔记·学习
SL_staff1 小时前
离散制造排程破局:JVS-APS 四步闭环落地实践(开发者视角)
java·产品·全栈
这个昵称不容易2 小时前
SpringBoot 美容院管理系统:232个Java文件315个接口,5套角色从预约到发货一整条链路
java·springboot·毕设
starzy19902 小时前
深入理解Scala嵌套函数:作用域封装之道
开发语言·scala
Sakura_j_h_2 小时前
从零写一个 MCP stdio Server:用 Python 校验博客文章格式
开发语言·python·自动化·ai工具·mcp·json-rpc
装不满的克莱因瓶2 小时前
【SpringAI Alibaba】ReactAgent记忆管理MessageTrimmingHook与MessageDeletionHook
java·人工智能·ai·ai编程·springai·springaialibaba