共享模型之内存

JMM

JMM:Java内存模型。定义了主存(所有线程共享的数据)、工作内存(每个线程对应的私有数据)的抽象概念。

JMM存在以下几个特征

  • 原子性:保证指令不会受到线程上下文切换所影响。
  • 可见性:保证指令不会被CPU缓存所影响。
  • 有序性:保证指令不会被CPU指令并行优化的影响。

可见性

public class test{static boolean run = true;public static void main(String[] args){new Thread(()->{while(run){//……}}).start();System.out.println("主线程结束子线程");run = false;}
}

主线程结束子线程

理论上来讲,当run改为false后,子线程也会跟着结束并结束程序运行。但是实际并不会结束程序,因此主线程结束并不会让子线程结束while循环。

这就是可见性一个体现,不受CPU缓存影响。从JMM解释来看。

解决方案

将run变量使用volatile(异变)关键词修饰。这样就不会从缓存区获取run值,而是从主存中获取。

volatile关键词用来修饰成员变量与静态成员变量,修饰局部变量没意义,因为局部变量是线程私有的,主存中都没带存的。

或是使用synchroized加锁后来修改run的值。因为synchroized在进入保护的代码前会废弃工作内存重新再去主存中读取。[拓展:synchronized在获取锁之前需要去主存中获取保护代码块所需要的变量存储在工作内存中,当释放锁时会将工作内存中的变量刷新到主存中]。

volatile只能解决可见性问题,并不能解决指令交错的问题,因此只适用于一个线程写多个线程读的场景,比如说两个线程分别进行i++与i--,并不能保证能够正常得出结果。

synchronized虽然可以解决原子性与可见性的问题但是属于重量级操作,性能比较低。

有序性

JVM在不影响程序运行的正确性的前提下,会进行指令重排。在多线程下可能会存在安全隐患。

一般情况下赋值操作,是不在乎谁先谁后,因此可以进行指令重排的。但是在多线程下,有时需要使用这些变量,那么可能会存在安全隐患。

public class demo6 {static int num;static boolean ready;static int result;public static void main(String[] args) {//线程1new Thread(() -> {if (ready) {result = num + num;} else {result = 1;}}).start();new Thread(() -> {num = 2;ready = true;}).start();}
}

对以上代码进行分析,原则上num与ready的赋值操作先后顺序是无所谓的。但是此时还存在线程1使用这两个变量,这两个变量的结果会对result的结果产生影响。

  • 结果1:result值为1,此结果是因为先对num赋值又或是没有赋值,此时ready为false,走了线程1中的else代码。
  • 结果2:result值为4,此结果是因为线程2对两个值进行了赋值后,线程1才进行执行走的是if中的代码。
  • 结果3:result值为0,此结果是对ready赋值后,但是num还没有进行赋值去执行线程1中的if中代码,导致result值为0。

以上是指令重排序带来的危害,无法预测程序的运行结果,禁止指令重排只需要对ready变量使用volatile修饰即可。

如何保证可见性

写屏障:在volatile修饰的变量之前包括volatile变量,对于共享变量的变动会同步到主存。

读屏障:对于volatile变量之后的变量读取需要从主存中读取。

如何保证有序性

写屏障:在指令重排序时,不会将写屏障之前的代码排序到写屏障之后。

读屏障:在指令重排序时,不会将读屏障之后的代码排序到读屏障之前。

写屏障只能保证能够读取到最新的数据,并不能解决指令交错的问题。有序性只能保重本线程中的代码不会被指令排序

关于单例模式中双重检查锁出现的问题

通过查看创建对象部分的字节码文件来看

前两行是获取单例对象并进行非空判断的字节码,如果不为空则跳转37处。这是第一层if判断

从6到36部分,是synchronized部分字节码,意思是获取类对象,复制一份存储在字符常量池中加锁,获取单例对象判断是否为空,为空则创建出一个对象,复制一份地址,根据地址调用构造器(21)后对单例对象进行复制后解锁,如果不为空跳转到37处。

问题在于21与24可能通过指令重排序后互换位置。那么在多线程中可能出现以下问题

线程2拿到了没有初始化的值去使用,造成空指针异常。

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

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

相关文章

4面试题--数据库(mysql)

执⾏⼀条 select / update 语句,在 MySQL 中发⽣了什么? Server 层负责建⽴连接、分析和执⾏ SQL。MySQL ⼤多数的核⼼功能模块都在这实现,主要包括 连接器,查询缓存(8.0版本去除,因为每次更新将会清空该…

4.前端--HTML标签3【2023.11.25】

1.表格 1.1表格的作用 表格的作用&#xff1a;表格主要用于显示、展示数据 1.2表格的基本格式 <table><tr><td>单元格内的文字</td><td>单元格内的文字</td>...</tr>... </table><table> </table> 是用于定义表…

搜索 C. Tic-tac-toe

Problem - C - Codeforces 思路&#xff1a;搜索&#xff0c;判断合法性。从起始态用搜索进行模拟&#xff0c;这样可以避免后面判断合法性这一繁琐的步骤。用一个map进行映射当前态及对应的结果。剪枝&#xff1a;如果当前字符串已经被搜索过&#xff0c;则直接跳过去。 代码…

【开源】基于JAVA的车险自助理赔系统

项目编号&#xff1a; S 018 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S018&#xff0c;文末获取源码。} 项目编号&#xff1a;S018&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 车…

1.3 取反器和8位取反器

取反器真值表: 取反开关输入输出011000110101 取反器相当于一个异或门 8位取反器

【深度学习】因果推断与机器学习

2023年初是人工智能爆发的里程碑式的重要阶段&#xff0c;以OpenAI研发的GPT为代表的大模型大行其道&#xff0c;NLP领域的ChatGPT模型火爆一时&#xff0c;引发了全民热议。而最新更新的GPT-4更是实现了大型多模态模型的飞跃式提升&#xff0c;它能够同时接受图像和文本的输入…

Co-DETR:DETRs与协同混合分配训练代码学习笔记

关于论文的学习笔记&#xff1a;Co-DETR:DETRs与协同混合分配训练论文学习笔记-CSDN博客 作者提出了一种新的协同混合任务训练方案&#xff0c;即Co-DETR&#xff0c;以从多种标签分配方式中学习更高效的基于detr的检测器。这种新的训练方案通过训练ATSS和Faster RCNN等一对多标…

【搜维尔科技】产品推荐:Virtuose 6D RV,大型工作空间触觉设备

Virtuose 6D RV为一款具有大工作空间并在所有6自由度上提供力反馈的触觉设备&#xff0c;设计专用于虚拟现实环境&#xff0c;特别适合于大型虚拟物体的处理。 Virtuose 6D RV是当今市场上唯一将高工作效率与高工作量相结合在一起的产品。6D RV特别适合于缩放与操纵等应用&…

【LeetCode】挑战100天 Day16(热题+面试经典150题)

【LeetCode】挑战100天 Day16&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-182.1 题目2.2 题解 三、面试经典 150 题-183.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

【数据结构实验】图(三)图的深度优先搜索(DFS)生成树

文章目录 1. 引言2. 深度优先搜索生成树3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现1. 数据结构2. 队列操作函数3. 广度优先搜索遍历4. 创建图5. 深度优先搜索算法6. 主函数及DFS主函数7. 输出生成树信息 3.3 …

WordPress安装AWS插件实现文本转语音功能

适用于 WordPress 的 AWS 插件示例演示了内容创建者如何轻松地为所有书面内容添加文本转语音功能。随着语音搜索的不断增加&#xff0c;以音频格式提供更多网站内容变得至关重要。通过添加语音功能&#xff0c;网站访客可以通过在线音频播放器和播客应用程序等新渠道使用您的内…

ART-PI开发套件-构建开发环境

1、env工具的安装与使用 env 是 RT-Thread 推出的开发辅助工具&#xff0c;针对基于 RT-Thread 操作系统的项目工程&#xff0c;提供编译构建环境、图形化系统配置及软件包管理功能。 其内置的 menuconfig 提供了简单易用的配置剪裁工具&#xff0c;可对内核、组件和软件包进行…

面试常见问题:什么是进程? 什么是线程?进程和线程有什么区别?

1.什么是进程&#xff1f; 进程是操作系统中一个程序在执行过程中的一个实例&#xff0c;每个进程都有自己独立的地址空间&#xff0c;进程间不共享内存。它是程序运行的最小内存单元&#xff1b; 进程特点&#xff1a; 1> 需要占用独立的内存空间&#xff1b; 2>可以并…

C语言—冒泡排序

方法一&#xff08;不使用函数解决&#xff09; #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int arr[]{15,52,23,0,5,6,45,8,9,10};int i0;int j0;for ( i 0; i < 9; i){int flag1; //flag判断数组元素是否有序&#xff0c;这里先假设…

VMware虚拟机安装华为OpenEuler欧拉系统

首先去欧拉官方网站下载openEuler的安装镜像&#xff1a; openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 我下载的是最新的23.03长期维护版本&#xff0c;架构选择x86_64。 创建新虚拟机&#xff1a;选择典型配置&#xff0c;点击下一步&#xff1a;选择下载的镜像文…

4.25每日一题(通过被积函数和积分区域(不等式)选正确的坐标系求二重积分)

一、正确画出积分区域&#xff1b;通过积分区域和被积函数选择方法 二、如何根据被积函数和积分区域正确选择通过极坐标还是根据直角坐标方程计算&#xff1a; &#xff08;1&#xff09;适合极坐标的积分区域&#xff1a;圆或者部分圆 &#xff08;2&#xff09;适合极坐标的…

公交路线查询系统

公交路线查询系统 一&#xff1a;目标一&#xff1a;类的定义构造方法 set和get方法&#xff1a;目标二&#xff1a;静态属性 静态方法 toString方法&#xff1a;目标三&#xff1a;抽象类的定义 抽象方法 实际应用&#xff1a;abstract class AbstractRoute{目标四&#xff1…

Hibernate 脏检查和刷新缓存机制

刷新缓存: Session是Hibernate向应用程序提供的操作数据库的主要接口,它提供了基本的保存,更新,删除和加载java对象的方法,Session具有一个缓存,可以管理和追踪所有持久化对象,对象和数据库中的相关记录对应,在某些时间点,Session会根据缓存中对象的变化来执行相关SQL语句,将对…

VMware OpenSLP漏洞解决方案

PS&#xff1a;早期为客户做VMware检测的方法&#xff0c;大家如有遇到可参考 OpenSLP堆溢出漏洞攻击大量ESXI服务器&#xff0c;该漏洞编号为CVE-2021-21974&#xff0c;由 OpenSLP 服务中的堆溢出问题引起 大于以下版本则不受影响 ESXi versions 7.x prior to ESXi7…

Java的内部类

文章目录 静态内部类(被static修饰的成员内部类)--可以实例化!获取静态内部类对象(把它当成外部类的成员)静态内部类可以声明普通成员变量和方法&#xff0c;而普通内部类不能声明static成员变量和方法静态内部类跟静态方法一样&#xff0c;只能访问静态的成员变量和方法&#…