Java中volatile关键字

保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。

1.volatile的可见性

一个典型的例子:永不停止的循环。

java 复制代码
    public class ForeverLoop {
        static boolean stop = false;
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stop = true;
                System.out.println("modify stop to true...");
            }).start();
            foo();
        }
        static void foo() {
            int i = 0;
            while (!stop) {
                i++;
            }
            System.out.println("stopped... c:" + i);
        }
    }

当执行上述代码的时候,发现foo()方法中的循环是结束不了的,也就说读取不到共享变量的值结束循环。

主要是因为在JVM虚拟机中有一个JIT(即时编辑器)给代码做了优化。

上述代码:

java 复制代码
while (!stop) {
i++;
}

在很短的时间内,这个代码执行的次数太多了,当达到了一个阈值,JIT就会 优化此代码,如下:

java 复制代码
while (true) {
i++;
}

当把代码优化成这样子以后,及时 stop 变量改变为了 false 也依然停止不了循环。

解决方案

在修饰 stop 变量的时候加上 volatile ,表示当前代码禁用了即时编辑器,问题就可以解决,代码如下:

java 复制代码
static volatile boolean stop = false;
2.指令重排序问题

出现该问题的原因

简单说就是JVM为了对代码进行优化提高性能会在不影响结果的情况下把代码执行顺序改变,但多线程就可能会出现结果不对的问题。

解决方案:添加volatile关键字

volatile原理就是加了一些屏障,使屏障后的代码一定不会比屏障前的代码先执行,从而实现有序性。

给变量y添加关键字的屏障添加的示意图:

写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下(阻止了 x = 1 走到 y = 1 之后)

读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上(同上,x 的读,走到 y 的读之前)

补充:为什么不能给x变量添加关键字?

给变量x添加关键字的屏障添加的示意图

这样显然是不行的,主要是因为下面两个原则:

1.写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下(这个屏障只能阻止 x = 1;y = 1;这两行代码上方的指令越过该屏障,它不能阻止 y = 1;往上走到 x = 1;之前!)

2.读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上

所以,现在我们就可以总结一个volatile使用的小妙招:

写变量让volatile修饰的变量的在代码最后位置

读变量让volatile修饰的变量的在代码最开始位置

相关推荐
小程故事多_8037 分钟前
破局 LLM 黑盒困局,Phoenix 凭全链路可观测,重构大模型应用工程化落地规则
java·前端·人工智能·重构·aigc
Albert Edison6 小时前
【Python】学生管理系统
开发语言·数据库·python
追随者永远是胜利者8 小时前
(LeetCode-Hot100)253. 会议室 II
java·算法·leetcode·go
宇木灵8 小时前
C语言基础-十、文件操作
c语言·开发语言·学习
追随者永远是胜利者8 小时前
(LeetCode-Hot100)207. 课程表
java·算法·leetcode·go
云泽8089 小时前
C++ 多态入门:虚函数、重写、虚析构及 override/final 实战指南(附腾讯面试题)
开发语言·c++
yanghuashuiyue10 小时前
lambda+sealed+record
java·开发语言
盟接之桥10 小时前
盟接之桥EDI软件:API数据采集模块深度解析,打造企业数据协同新引擎
java·运维·服务器·网络·数据库·人工智能·制造
yzx99101311 小时前
Python数据结构入门指南:从基础到实践
开发语言·数据结构·python
HoneyMoose11 小时前
Spring Boot 2.4 部署你的第一个 Spring Boot 应用需要的环境
java