行业资讯 2025年08月6日
0 收藏 0 点赞 396 浏览 1861 个字
摘要 :

文章目录 代码案例 原因分析 解决方法 注意 synchronized关键字保证可见性 总结 本文主要讲解关于Java并发编程中volatile关键字可见性问题的详解相关内容,让我们来一……




  • 代码案例
  • 原因分析
  • 解决方法
  • 注意
  • synchronized关键字保证可见
  • 总结

本文主要讲解关于Java并发编程volatile关键字可见性问题的详解相关内容,让我们来一起学习下吧!

volatile关键字在java并发编程中非常重要,但也有很多同学搞不清它的具体作用及其原理,这篇文章主要讲解volatile的其中一个特性——保证可见性,并简单讲解下其中的原理,后期再将其深入的原理,以及禁止指令重排序问题。

代码案例

为了方便大家理解volatile保证可见性问题,我们先看下一个代码案例:

public class VolatileDemo {
    static boolean run  = true;
    public static void main(String[] args) throws InterruptedException {
        // 创建t线程
        new Thread(()->{
            while (run) {
                // do something
                // 这里做输出或不要打印日志,后面解释原因
            }
        },\"t\").start();
        // 主线程休眠1s
        Thread.sleep(1000);
        // 将run置为false
        run= false;
    }
}

你觉得1s后,主线程将run置为false,t线程while判断会结束运行吗?正常逻辑,我们认为t线程结束循环,但事实是t线程已经陷入死循环,无法结束,而原因就是,主线程对run的修改对于t线程是不可见的,那么问题就来了,为什么不可见?主要原因在于每个线程都有自己独立的工作内存,而成员变量是存放在主内存中的,每个线程在运行时会将成员变量的副本拷贝一份进自己的工作内存,我们一起看下图解。

原因分析

我们来分析一下:

1. 初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存。Java并发编程volatile关键字可见性详解

2. 因为 t 线程要频繁从主内存中读取 run的值,JIT 编译器会将 run的值缓存至自己工作内存中的高速缓存中,减少对主存中 run的访问,提高效率Java并发编程volatile关键字可见性详解

3. 1 秒之后,main主线程修改了 run的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值

Java并发编程volatile关键字可见性详解

解决方法

使用volatile(易变关键字)修饰线程间共享变量,使之在线程之间可见。

volatile可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存。

我们修改代码之后:

static volatile boolean run = true;

再去测试,发现1s后t线程成功结束了。

注意

在上面的代码中do something位置,我们说“这里做输出或不要打印日志”,只要你在里面调用了System.out.println或log.debug等,就会发现,t线程会停止,run变成可见的了,而原因就在于println的源码中有synchronized同步了:

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
}

这也是下面要讲的。

synchronized关键字保证可见性

其实synchronized 关键字也可以保证可见性,我们看下代码:

public class VolatileDemo {
    static boolean run  = true;
    // 锁对象
    final static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {

        // 创建t线程
        new Thread(()->{
            while (true) {
                synchronized (lock) {
                    if(!run) {
                        break;
                    }
                    // do something
                }
            }
        },\"t\").start();
        // 主线程休眠1s
        Thread.sleep(1000);
        // 将run置为false
        synchronized (lock) {
            run = false;
        }
    }
}

以上代码也可以实现1s后t线程结束运行。

但相比于 synchronized 关键字(重量级锁)对性能影响较大,更推荐使用 volatile 关键字保证可见性。由于使用 volatile 不会引起上下文的切换和调度,所以 volatile 对性能的影响较小,开销较低。

总结

以上就是Java并发编程volatile关键字可见性详解,至于为什么volatile能保证可见性,我们后面有机会再讲,希望本文对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9901.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号