Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >并发编程系列之变量可见性问题探究

并发编程系列之变量可见性问题探究

作者头像
SmileNicky
发布于 2021-11-17 02:49:22
发布于 2021-11-17 02:49:22
30100
代码可运行
举报
文章被收录于专栏:Nicky's blogNicky's blog
运行总次数:0
代码可运行

并发编程系列之变量可见性问题探究

1、什么是并发中的变量可见性问题

以例子的形式看看,定义一个变量,先用static修饰,在主线程修改之后,看看在新开的子线程里能被看到?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Example {
private static boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                        i++;
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("设置flag");
    }
}

执行,控制台打印:

设置flag

ps:主线程对flag变量进行修改,子线程是不能看到的,所以里面一直在循环,不能打印统计数据值。然后怎么才能让并发线程看见?

  • 方式1:使用volatile关键字
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Example {
private static volatile boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                        i++;
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("设置flag");
    }
}

控制台打印:

设置flag 72071943

  • 方式2:使用synchronized同步锁
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Example {
private static boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                    synchronized (this) {
                        i++;
                    }
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("设置flag");
    }
}

控制台打印:

设置flag 86726163

2、什么是Java内存模型?

解答这个问题,需要涉及到Java的内存模型,如下所示,Java内存模型及操作规范:

  1. 共享变量都是放在主内存中的
  2. 每个线程都有自己的工作内存,线程只可操作自己的工作内存
  3. 线程要操作共享变量,需要从主内存中读取到工作内存,改变值之后要从工作内存同步到主内存
  • Java内存模型的同步交换协议,规定了8种原子操作

原子操作:不可被中断的一个或一系列操作

  1. lock(锁定):将主内存中的变量锁定,为一个线程所独占
  2. unlock(解锁):将lock加的锁解除,其他的线程有机会访问此变量
  3. read(读取):作用于主内存变量,将主内存中的变量值读取到工作内存
  4. load(加载):作用于工作内存,将read读取到的值保存到工作内存中的变量副本
  5. use(使用):作用于工作内存变量,将值传递给线程的代码执行引擎
  6. assign(赋值):作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本
  7. store(存储):作用于工作内存变量,将变量副本的值传送到主内存中
  8. write(写入):作用于主内存变量,将store传送过来的值写入到主内存的共享变量中
  • Java内存模型的同步交互协议,执行上述8种原子操作时必须满足如下规则
  1. 不允许read和load,store和write操作之一单独出现。即不允许加载或同步工作到一半。
  2. 不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变之后,必须将数据同步回主内存
  3. 不允许一个线程无原因地(无assign操作)将数据从工作内存同步到主内存中。
  4. 一个新的变量可能在主内存中诞生。
  5. 一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次lock之后必须要执行相同次数unlock操作,变量才会解锁
  6. 如果对一个对象进行lock操作,那么会清空工作内存变量中的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始变量的值
  7. 如果一个对象事先没有被lock,就不允许对其进行unlock操作,也不允许去unlock一个被其他线程锁住的变量。
  8. 对一个变量执行unlock操作之前,必须将此变量同步回主内存中(执行store、write)
  • Java内存模型的同步协议,操作规范
  1. 将一个变量从主内存复制到工作内存要顺序执行read、load操作;要将变量从工作内存同步回主内存要用store、write操作。只要求顺序执行,不一定是连续执行

图引用网上资料:

3、保证变量可见性的方法

  1. final变量
  2. synchronized
  3. volatile修饰

4、Synchronized怎么做到可见性

  • synchronized语义规范:
  1. 进入同步块前,先清空工作内存中的共享变量,从主内存加载
  2. 解锁前,必须将修改的共享变量同步回主内存
  • synchronized是如何做到线程安全的?
  1. 锁机制保护共享资源,只有获得锁的线程才能操作共享资源
  2. synchronized语义规范保证了修改共享资源后,会同步回主内存,就做到了线程安全

5、volatile关键字解密

  • volatile语义规范:
  1. 使用volatile变量时,必须重新从主内存加载到工作内存,并且read、load是连续的
  2. 修改volatile变量后,必须马上同步回主内存,并且store、write是连续的
  • volatile可以做到线程安全? 不能,因为volatile没有锁机制,线程是可以并发操作共享资源的
  • volatile相对synchronized有什么优点?
  1. 使用volatile比synchronized简单
  2. volatile性能比synchronized好
  • volatile的使用场景?
  1. volatile只能修饰成员变量
  2. 在多线程并发的场景才使用
  • volatile支持并发编程三大特效? 并发编程三大特效:原子性、有序性、可见性。
  1. 可见性:volatile和synchronized关键字一样,都可以保证可见性
  2. 有序性:volatile可以保证有序性,避免指令编排的情况,依赖于操作系统的内存屏障
  3. 原子行 :volatile只能保证单个操作的原子性,不能保证一系列操作的原子性,不能保证线程安全,所以说volatile不能保证原则性
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/11/15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
并发编程系列之什么是Java内存模型?
Java内存模型简称JMM(Java Memory Model),JMM是和多线程并发相关的一组规范。各个jvm实现都要遵循这个JMM规范。才能保证Java代码在不同虚拟机顺利运行。因此,JMM 与处理器、缓存、并发、编译器有关。它解决了CPU 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。
SmileNicky
2021/11/24
2.1K0
并发编程系列之什么是Java内存模型?
请谈谈你对线程可见性及volatile关键字的理解?
引言可见性问题基本数据类型的可见性问题引用数据类型可见性问题引用可见性问题成员变量可见性问题可见性问题总结Java内存模型CPU与内存之间的爱恨情仇Java内存模型主存与工作内存间的交互规则Volatile变量特殊规则先行发生原则对先行发生原则的理解volatile的使用保证变量可见性防止指令重排案例解决
敲得码黛
2021/02/22
4950
请谈谈你对线程可见性及volatile关键字的理解?
2.1 并发编程之java内存模型JMM & synchronize & volatile详解
一. 什么是JMM模型? JMM描述的是一组规范, 它类似于cpu和内存的架构. 这套模型是为了适配不同的操作系统, 不同的硬件结构, 屏蔽掉底层的微小的差异. jvm为什么可以跨平台呢? 有一方面就
用户7798898
2020/09/27
4040
2.1 并发编程之java内存模型JMM & synchronize & volatile详解
Java三大性质总结:原子性、可见性以及有序性
1. 三大性质简介 在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及happens-before规则,三条性质:原子性,有序性和可见性。
李红
2019/11/20
1.7K0
死磕 java同步系列之JMM(Java Memory Model)
Java内存模型是在硬件内存模型上的更高层的抽象,它屏蔽了各种硬件和操作系统访问的差异性,保证了Java程序在各种平台下对内存的访问都能达到一致的效果。
彤哥
2019/07/08
3290
死磕 java同步系列之JMM(Java Memory Model)
JAVA 内存模型 (Java Memory Model,JMM)
是在硬件内存模型基础上更高层的抽象,它屏蔽了各种硬件和操作系统对内存访问的差异性,从而实现让Java程序在各种平台下都能达到一致的并发效果。
微风-- 轻许--
2020/03/25
4180
浅谈Volatile与JMM
之前看关于volatile的文章好多都没有讲到JMM,在并发编程中了解JMM对我们开发有很大帮助,故自己了总结一下volatile与JMM那密不可分的关系。
Coder昊白
2023/11/22
2120
浅谈Volatile与JMM
深入理解JVM(③)学习Java的内存模型
Java内存模型(Java Memory Model)用来屏蔽各种硬件和操作系统的内存访问差异,这使得Java能够变得非常灵活而不用考虑各系统间的兼容性等问题。定义Java内存模型并非一件容易的事情,从Java出生开始经过长时间的验证和修补,直至JDK5发布后Java内存模型才终于成熟、完善起来了。
纪莫
2020/07/10
3860
JAVA系列之内存模型(JMM)
Java内存模型是在硬件内存模型上的更高层的抽象,它屏蔽了各种硬件和操作系统访问的差异性,保证了Java程序在各种平台下对内存的访问都能达到一致的效果。 Java内存模型是不可见的,它并不是一个真实的东西,它只是一个概念、一个规范。
夕阳也是醉了
2023/10/16
2170
JAVA系列之内存模型(JMM)
Java底层-01-Java内存模型
Java内存模型是一种抽象的规则或规范,定义了程序中存在竞争现象的对象(包括实例字段、静态字段和数组对象,不包括局部变量,形式参数;后者是线程私有,不存在竞争问题)的访问方式。
devi
2021/08/18
5140
Java并发:volatile关键字详解
volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易完全被正确、完整地理解,以至于许多程序员都习惯不去使用它,遇到需要处理多线程数据竞争问题的时候一律使用synchronized来进行同步。了解volatile变量的语义对了解多线程操作的其他特性很有意义,在本文中我们将介绍volatile的语义到底是什么。由于volatile关键字与Java内存模型(Java Memory Model,JMM)有较多的关联,因此在介绍volatile关键字前我们会先介绍下Java内存模型。
Java架构师必看
2021/05/18
6140
Java并发:volatile关键字详解
Android并发编程 开篇
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!
LoveWFan
2018/09/29
4910
Android并发编程 开篇
Java内存模型
多任务处理在现代计算机操作系统中几乎已经是一项必备的功能了。计算机cpu的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O、网络通信或数据库访问上。如果不希望处理器在大部分时间里都处于等待其他资源的状态,那么并发的处理多项任务是最容易想到、也是非常有效的“压榨”处理器运算能力的一种手段。 服务端是java语言最擅长的领域之一。如果写好并发应用程序是服务端程序开发的难点之一,java语言和虚拟机提供了许多工具来帮助程序员降低门槛,并且各种中间件服务器、各类框架都努力的替程序员处理更多的并发希捷,使得程序员在编码过程中更关注业务逻辑。但无论语言、中间件和框架多么先进,都不能独立的完成所有并发处理的事情,所以了解并发的内幕也是一个高级程序员不可缺少的课程。 高效并发是本教程的最后一部分,主要讲解虚拟机如何实现多线程、多线程之间由于共享和竞争数据而导致的一系列问题及解决方案。
栋先生
2018/09/29
8120
Java内存模型
快速掌握并发编程---深入了解volatile
今天聊得这个volatile是一个轻量级的synchronized,它在多线程开发中保证了共享变量的“可见性”。
田维常
2020/11/03
6040
快速掌握并发编程---深入了解volatile
JUC并发编程(二)认识volatile,单例模式,各种锁
JMM : Java内存模型,不存在的东西,概念!约定! 关于JMM的一些同步的约定: 1. 线程解锁前,必须把共享变量立刻刷回主存。 2. 线程加锁前,必须读取主存中的新值到工作内存中! 3. 加锁和解锁是同一把锁
HcodeBlogger
2020/07/14
3340
JUC并发编程(二)认识volatile,单例模式,各种锁
高并发之——多线程的三大特性(原子性、可见性和有序性)
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
2.9K0
面试官问我什么是JMM
JMM就是Java内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果。
java技术爱好者
2020/09/24
9080
面试官问我什么是JMM
Java内存模型以及线程安全的可见性问题
首先Java内存模型(JMM)和JVM运行时数据区并不是一个东西,许多介绍Java内存模型的文章描述的堆,方法区,Java虚拟机栈,本地方法栈,程序计数器这东西并不是Java内存模型的内容而是JVM运行时数据区的内容。 要理解二者的区别就要了解《Java虚拟机规范》和《Java语言规范》。我们知道Java虚拟机上并不知只有Java语言,像JRuby, ,Scala,Kotlin,Groovy等也都运行在Java虚拟机上,而这些语言想要在Java虚拟机上运行就要遵守《Java虚拟机规范》,而JVM运行时数据区就是《Java虚拟机规范》的内容。而《Java语言规范》就只是针对Java语言的规范,它对Java内存模型做了详细的描述。
CodingDiray
2019/09/25
9040
Java内存模型以及线程安全的可见性问题
学妹教你并发编程的三大特性:原子性、可见性、有序性
在并发编程中有三个非常重要的特性:原子性、有序性,、可见性,学妹发现你对它们不是很了解,她很着急,因为理解这三个特性对于能够正确地开发高并发程序有很大的帮助,接下来的面试中也极有可能被问到,小学妹就忍不住开始跟你逐一介绍起来。
万猫学社
2022/04/22
2990
学妹教你并发编程的三大特性:原子性、可见性、有序性
简单了解下Java并发编程对象共享的可见性问题
可见性是一个复杂的属性,因为它经常违背我们的直觉。在单线程环境中,如果先写入某个变量的值,然后在没有其他写入操作的情况下读取该变量,程序总能得到相同的值,这是符合我们的期望的。然而,在多线程环境中,当读操作和写操作在不同的线程中执行时,情况却并非如此。通常情况下无法确保执行读操作的线程能够及时地看到其他线程写入的值,有时甚至是根本不可能的。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制来进行严格的同步和协调。
codetrend
2024/07/26
1120
相关推荐
并发编程系列之什么是Java内存模型?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验