并发编程三大特性之可见性

一、什么是可见性?

               可见性问题是基于CPU位置出现的,cpu处里速度非常快,相对CPU来说去主内存

      获取数据这个事情太慢了,CPU就提供了 L1,L2,L3的三季缓存,每次去主内存拿完

      数据后,数据就先存储到三级缓存,然后cpu再去三级缓存取数据,效率肯定会提升;

      三级缓存就是是每个线程的工作内存,是相互独立的。

              这就带来了一个问题:现在CPU都是多核,每个线程的工作内存(CPU三级缓存)都

      是独立的,会告知每个线程做修改时,只修改自己的工作内存,数据没有及时同步到主内存

      ,从而导致数据不一致的问题

     线程运行时数据处里过程如下:

             

     使用下边代码来验证数据可见性的问题,代码如下:

             

             

二、解决可见性问题的方式

       1、volatile 

             volatile是一个关键字,用于修饰成员变量

              如果属性被volatile修饰,相当于告诉cpu,对于当前属性的操作,不允许使用CPU

              缓存(即线程私有内存),必须去操作主内存。

              volatile的内存语义:

                    (1)volatile 属性被写:当写一个volatile变量,JMM会将当前线程的CPU缓存的

                                 数据及时刷新到主内存中

                    (2)volatile 属性被读:当读一个volatile变量,JMM会将当前线程对应的CPU缓存

                                 设置为无效,必须从主内存读取数据。

               其实变量加了volatile就是告诉cpu,对当前变量的读写操作,不允许使用CPU缓存;加

               了volatile 的变量会在编译成汇编之后追加一个lock前缀,CPU执行这个指令时,如果

                带有lock前缀会做2件事:

                       (1)将当处理器缓存行的数据写回到主内存

                       (2)这个写会的数据,在其他的CPU内核的缓存中,直接无效

                  总结:volatile 就是让CPU每次操作这个数据时,必须立即同步到主内存,以及从主内

                             存读取数据。

                             在代码中若先对volatile属性进行操作,则其他属性也是可见性的。

               针对上边的代码,采用volatile解决可见性问题实现如下:

/******************************************************** 验证线程的可见性问题* 每个线程都有自己的私有内存,相互独立,线程运行时处里的是自己私有内存的数据** 使用volatile解决内存可见性*******************************************************/
public class Test02 {private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag){}System.out.println("子线程 t1 中 flag改变");});t1.start();//预期:flag修改成false后,不影响线程t1的运行Thread.sleep(100);//修改flag 的值flag = false;System.out.println(" main 线程中修改 flag = false");}
}//方案二:在代码中若先对volatile属性进行操作,则其他属性也是可见性的
public class Test02 {private volatile static int i= 0;private  static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag){//todo: 在代码中若先对volatile属性进行操作,则其他属性也是可见性的i++;}System.out.println("子线程 t1 中 flag改变");});t1.start();//预期:flag修改成false后,不影响线程t1的运行Thread.sleep(100);//修改flag 的值flag = false;System.out.println(" main 线程中修改 flag = false");}
}

       2、synchronized

              synchronized也是可以解决可见性问题。

              synchronized内存语义:

                      如果涉及到了synchronized 的同步代码块或者同步方法,获取资源之后,将内部

                      涉及到的变量从CPU缓存(线程私有内存)中移除,必须重新去主内存中取数据;

                      而且在释放锁之后,会立即将CPU缓存中的数据同步到主内存中。

              使用 synchronized 解决内存可见性 示例代码如下:

                           

/******************************************************** 验证线程的可见性问题* 每个线程都有自己的私有内存,相互独立,线程运行时处里的是自己私有内存的数据** 使用synchronized解决内存可见性*******************************************************/
public class Test03 {private  static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag){/** 问题:为什么这里synchronized 放在while循环里边,不放在外边?*     因为线程 在获取到 synchronized 锁之后才会从主内存拿数据,若把synchronized 放在while外边*     则只会从主内存拿一次数据,后边不能监听到变量 flag 变化*/synchronized (Test03.class){//todo}}System.out.println("子线程 t1 中 flag改变");});t1.start();//预期:flag修改成false后,不影响线程t1的运行Thread.sleep(100);//修改flag 的值flag = false;System.out.println(" main 线程中修改 flag = false");}
}

       3、Lock

             Lock锁保证可见性的方式和synchronized 完全不同,synchronized是基于内存语义在获取

             锁和释放锁对CPU缓存做一个同步到主内存的操作。

             lock 锁是基于volatile实现的,lock 锁内部进行加锁和释放锁时,会对一个volatile修饰的属

             state做加减操作。

             如果对volatile属性进行写操作,CPU会执行带有lock前缀的指令,会将CPU缓存的数据立

             即同步到主内存,同时也会将其他非volatile属性页一起同步到主内存。还会将其他CPU缓

             存行 中这个volatile数据设置为无效,必须从主内存重新拉取。

             lock解决可见性示例代码如下:

                  

/******************************************************** 验证线程的可见性问题* 每个线程都有自己的私有内存,相互独立,线程运行时处里的是自己私有内存的数据** 使用lock解决内存可见性*******************************************************/
public class Test04 {private  static boolean flag = true;private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag){lock.lock();try {//。。。。。}finally {lock.unlock();}}System.out.println("子线程 t1 中 flag改变");});t1.start();//预期:flag修改成false后,不影响线程t1的运行Thread.sleep(100);//修改flag 的值flag = false;System.out.println(" main 线程中修改 flag = false");}
}

       4、final

             final本质上说并不能像synchronized和volatile 那种形式保证可见性,final修饰的属性在

             运行期间是不允许修改的,这样一来就间接保证了可见性。

             final与volatile不允许同时修饰一个属性,final修饰的属性不允许被修改,而volatile保证

             每次从主内存读取数据,并且volatile会影响一定性能,就不需要同时修饰。

              final与volatile同时修饰属性会报错,如下图所示:

                   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/798234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MBTI职业性格测试:发现你的内在力量,探索职业新世界!

MBTI简介 MBTI是根据荣格的心理类型理论著成,已经成为权威的性格测试。 MBTI(Myers-Briggs Type Indicator),是一种迫选型、自我报告式的性格评估测试,用以衡量和描述人们在获取信息、作出决策、对待生活等方面的心理…

【从零到一手撕脚手架 | 第五节】自定义命令行下载cli工具

【从零到一手撕脚手架 | 第五节】自定义命令行下载cli工具 Hello大家好我是⛄,之前我们已经成功搭建了一套Vue3的快速开发模板,提高我们搭建新项目的效率,但是当我们的模板逐渐增多,如果依然使用git clone的方式去下载模板较为繁琐…

使用LIKE进行模糊查询

查询包含字符‘e’的信息 % 代表不确定个数的字符(零个或多个) SELECT employee_id, first_name FROM employees WHERE first_name LIKE %e%; 查询以字符‘e’开头的字符 SELECT employee_id, first_name FROM employees WHERE first_name LIKE e%; _…

腾讯云2024年优惠券领取及使用常见问题

腾讯云作为国内领先的云计算服务提供商,经常会推出各种优惠活动,以此来吸引用户上云。其中,优惠券作为一种常见的促销方式,受到了众多用户的青睐。然而,在领取和使用优惠券的过程中,大家可能会遇到一些常见…

1.《C语言》—— [常见概念]

前言: C语言是学习编程的一门语言,C语言概念少,词汇少,包含了基本的编程元素,再后来的很多语言如(C,Java)等都参考了C语言,所以想要学好编程,C语言是必不可少的一门&…

77、WAF攻防——权限控制代码免杀异或运算变量覆盖混淆加密传参

文章目录 WAF规则webshell免杀变异 WAF规则 函数匹配 工具指纹 webshell免杀变异 php 传参带入 eval可以用assert来替换,assert也可以将字符串当作php代码执行漏洞 php 变量覆盖 php 加密 使用加密算法对php后门进行加密 php 异或运算 简化:无字符webshellP 无数字字母rc…

《米小圈上学记》——让孩子爱上阅读一点也不难!

阅读能力的培养是小学语文素质教育重要的组成部分,阅读能力的高低,直接关系到学生的理解能力、运用知识的能力以及表达能力的提升。提高小学生的阅读能力不仅关系到小学生语文素养的培养,而且对他们开阔视野、提高内涵、增加底蕴、放飞心灵有…

hibernate执行外部sql

开发背景 公司多年前项目,使用hibernate作为持久层,部分sql查询采用spring的JdbcTemplate,sql穿插在java代码中。因此,需要统一使用hibernate,并且sql部分需要类似Mybatis一样从文件中读取。由于引入Mybatis需要对项目…

哈希存节点,双dp数组存选和不选

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。 除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连…

Linux:安装zabbix-agent被监控端(2)

本章是结合着上一篇文章的续作 Linux:部署搭建zabbix6(1)-CSDN博客https://blog.csdn.net/w14768855/article/details/137426966?spm1001.2014.3001.5501本章将在两台centos部署agent端,然后使用server进行连接监控 agent1 在1…

11、子串-滑动窗口最大值

题解: 双端队列是一种特殊的队列,允许你在队列的两端进行插入和删除操作。在滑动窗口问题中,我们使用它来存储可能是当前窗口最大值的元素的索引。 维护队列的顺序: 当新元素进入窗口时,我们将它与队列尾部的元素进…

RSA相关学习存档

什么是RSA,以及RSA算法: https://zhuanlan.zhihu.com/p/450180396 https://blog.csdn.net/m0_51607907/article/details/123884953 https://blog.csdn.net/firechungelaile/article/details/39974379 https://blog.csdn.net/lesczx/article/details/…

【LeetCode热题100】4. 寻找两个正序数组的中位数(二分)

一.题目要求 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 二.题目难度 困难 三.输入样例 示例 1: 输入:nums1 [1,3…

子集(迭代)(leetcode 78)

核心逻辑&#xff1a; 根据子数组包含的元素个数迭代&#xff1a; 现有子集的基础上通过添加这个新元素来翻倍子集的数量 f(n)2f(n−1) vector<vector<int>> subsets(vector<int>& nums) {vector<vector<int>> ans;int i,j,k;ans.p…

《科技创业月刊》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答&#xff1a;问&#xff1a;《科技创业月刊》是什么级别的刊物&#xff1f; 答&#xff1a;省级&#xff0c;主管单位&#xff1a; 湖北省科学技术厅 &#xff1b;主办单位&#xff1a;湖北省科技信息研究院 问&#xff1a;《科技创业月刊》是c刊吗&#xff1f; 答&…

spring面试八股

常用的注册bean的方式 ComponentScan扫描到的service和Controller等的注解 Configration配置类或者是xml文件的定义。 spring中有几种依赖注入的方式 1.构造器注入。 2.setter方法注入。 3.使用field属性的方式注入。 applicationContext是什么 spring bean spring aop Aop…

Linux命令-dpkg-deb命令(Debian Linux下的软件包管理工具)

说明 dpkg-deb命令 是Debian Linux下的软件包管理工具&#xff0c;它可以对软件包执行打包和解包操作以及提 供软件包信息。 语法 dpkg-deb(选项)(参数)选项 -c&#xff1a;显示软件包中的文件列表&#xff1b; -e&#xff1a;将主控信息解压&#xff1b; -f&#xff1a;把…

java种Hutools常用方法

目录 一、依赖二、Convert三、DateUtil四、StrUtil五、ReflectUtil六、IdUtil七、RandomUtil八、BeanUtil九、JSONUtil Hutool是一个小而全的Java工具类库&#xff0c;通过静态方法封装&#xff0c;降低相关API的学习成本&#xff0c;提高工作效率&#xff0c;使Java拥有函数式…

2-django、http、web框架、django及django请求生命周期、路由控制、视图层

1 http 2 web框架 3 django 3.1 django请求生命周期 4 路由控制 5 视图层 1 http #1 http 是什么 #2 http特点 #3 请求协议详情-请求首行---》请求方式&#xff0c;请求地址&#xff0c;请求协议版本-请求头---》key:value形式-referer&#xff1a;上一次访问的地址-user-agen…

【算法-数组】移除元素

这里写自定义目录标题 暴力解法双指针思路 leecode27 : https://leetcode.cn/problems/remove-element/submissions/521113648/ 暴力解法 循环匹配&#xff0c;每次匹配到就将数组匹配到的元素的后面元素向前移动一位 【注意】 注意最后一位元素&#xff0c;避免数组越界 pu…