Java多线程——synchronized、volatile 保障可见性

简介: Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。

引出

Java多线程——synchronized、volatile 保障可见性


synchronized、volatile 保障可见性

  • 原子性:在一次或者多次操作时,要么所有操作都被执行,要么所有操作都不执行。
  • 可见性:当一个线程对共享变量进行修改后,另外一个线程可以立即看到该变量修改后的最新值。
  • 有序性:程序执行的顺序按照代码的先后顺序执行。

synchronized关键字和Lock相关的工具类可以保证原子性、可见性和有序性,volatile关键字可以保证可见性和有序性,不能保证原子性。

可见性:synchronize、volatile

原子性:synchronize

volatile保证数据的可见性,但是不保证原子性(多线程进行写操作,不保证线程安全);而synchronized是一种排他(互斥)的机制。

主线程无法感知t1线程修改了flag值:

java

代码解读

复制代码

private static Boolean flag = false;
public static void main(String[] args) throws Exception {
    Thread t1 = new Thread(()->{
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
    });
    t1.start();
    while (true) {
        if (flag) {
            System.out.println("线程修改了flag");
            break;
        }
    }
}

解决1:synchronized。 程序执行synchronized后,会清空工作内存副本数据,之后就从新从主内存获取数据

kotlin

代码解读

复制代码

while (true) {
    synchronized (flag) { 
        if (flag) {
            System.out.println("线程修改了flag");
            break;
        }
    }
}

某一个线程进入synchronized代码块前后,执行过程入如下:

线程获得锁

清空工作内存

从主内存拷贝共享变量最新的值到工作内存成为副本

执行代码

将修改后的副本的值刷新回主内存中

线程释放锁

解决2:volatile 也可以保证多线程之间访问共享变量时的可见性。

java

代码解读

复制代码

public class App10 {
    private static volatile Boolean flag = false;
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
        });
        t1.start();
        while (true) {
            if (flag) {
                System.out.println("线程修改了flag");
                break;
            }
        }
    }
}

子线程t从主内存读取到数据放入其对应的工作内存

将flag的值更改为true,但是这个时候flag的值还没有写会主内存

此时main方法main方法读取到了flag的值为false

当子线程t将flag的值写回去后,失效其他线程对此变量副本

再次对flag进行操作的时候线程会从主内存读取最新的值,放入到工作内存中

总结: volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。


转载来源:https://juejin.cn/post/7414730597828755471

相关文章
|
5天前
|
调度 云计算 芯片
云超算技术跃进,阿里云牵头制定我国首个云超算国家标准
近日,由阿里云联合中国电子技术标准化研究院主导制定的首个云超算国家标准已完成报批,不久后将正式批准发布。标准规定了云超算服务涉及的云计算基础资源、资源管理、运行和调度等方面的技术要求,为云超算服务产品的设计、实现、应用和选型提供指导,为云超算在HPC应用和用户的大范围采用奠定了基础。
179567 18
|
12天前
|
存储 运维 安全
云上金融量化策略回测方案与最佳实践
2024年11月29日,阿里云在上海举办金融量化策略回测Workshop,汇聚多位行业专家,围绕量化投资的最佳实践、数据隐私安全、量化策略回测方案等议题进行深入探讨。活动特别设计了动手实践环节,帮助参会者亲身体验阿里云产品功能,涵盖EHPC量化回测和Argo Workflows量化回测两大主题,旨在提升量化投研效率与安全性。
云上金融量化策略回测方案与最佳实践
|
14天前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
9189 23
|
18天前
|
Cloud Native Apache 流计算
资料合集|Flink Forward Asia 2024 上海站
Apache Flink 年度技术盛会聚焦“回顾过去,展望未来”,涵盖流式湖仓、流批一体、Data+AI 等八大核心议题,近百家厂商参与,深入探讨前沿技术发展。小松鼠为大家整理了 FFA 2024 演讲 PPT ,可在线阅读和下载。
4883 12
资料合集|Flink Forward Asia 2024 上海站
|
18天前
|
自然语言处理 数据可视化 API
Qwen系列模型+GraphRAG/LightRAG/Kotaemon从0开始构建中医方剂大模型知识图谱问答
本文详细记录了作者在短时间内尝试构建中医药知识图谱的过程,涵盖了GraphRAG、LightRAG和Kotaemon三种图RAG架构的对比与应用。通过实际操作,作者不仅展示了如何利用这些工具构建知识图谱,还指出了每种工具的优势和局限性。尽管初步构建的知识图谱在数据处理、实体识别和关系抽取等方面存在不足,但为后续的优化和改进提供了宝贵的经验和方向。此外,文章强调了知识图谱构建不仅仅是技术问题,还需要深入整合领域知识和满足用户需求,体现了跨学科合作的重要性。
|
26天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
14天前
|
人工智能 容器
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
本文介绍了如何利用千问开发一款情侣刮刮乐小游戏,通过三步简单指令实现从单个功能到整体框架,再到多端优化的过程,旨在为生活增添乐趣,促进情感交流。在线体验地址已提供,鼓励读者动手尝试,探索编程与AI结合的无限可能。
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
|
13天前
|
消息中间件 人工智能 运维
12月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
1023 67