Java 内存模型

1、并发模型编程的分类

  在并发模型编程中,我们需要解决两个关键问题:线程之间如何通信以及线程之间如何同步。线程之间的通信包括两种:共享内存和消息传递。

  Java并发采用的是共享内存模型。

2、Java内存模型的抽象

  Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。此处的变量与Java编程时所说的变量不一样,指包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。

  Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存(可以与前面将的处理器的高速缓存类比),线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成,线程、主内存和工作内存的交互关系如下图所示。

3 、内存间交互操作

  关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:

  • lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

  如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b, load a。Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:

  • 不允许read和load、store和write操作之一单独出现
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,不许先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线成对其进行lock操作,lock和unlock必须成对出现
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把次变量同步到主内存中(执行store和write操作)。

4、 重排序

  在执行程序时为了提高性能,编译器和处理器经常会对指令进行重排序。重排序分成三种类型:

  1. 编译器优化的重排序。编译器在不改变单线程程序语义放入前提下,可以重新安排语句的执行顺序。
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  3. 内存系统的重排序。由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

从Java源代码到最终实际执行的指令序列,会经过下面三种重排序:

为了保证内存的可见性,Java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。Java内存模型把内存屏障分为LoadLoad、LoadStore、StoreLoad和StoreStore四种:

5、 原子性、可见性与有序性

 原子性:哪些指令必须是不可分割的。在Java内存模型中,这些规则需声明仅适用于-—实例变量和静态变量,也包括数组元素,但不包括方法中的局部变量-—的内存单元的简单读写操作。

/** * 非线程安全(原子性) * @author Snway * */  
public class Atomicity {  private int value;  public int getNextValue() {  return value++; // 非原子操作(包含三个操作,即读取value,将value加1,并将计算结果写入value)  
    }  
} 

   在Atomicity类中,如果执行时机不对,那么两个线程在调用getNextValue时会得到相同的值。如图下所示,给出了这种错误情况。虽然递增运算value++看上去是单个操作,但事实上它包含三个独立的操作:读取value,将value+1,并将计算结果写入value。由于运行时可能将多个线程之间的操作交替执行,因此这两个线程可能同时执行读操作,从而使它们得到相同的值,并都将这个值加1.结果就是,在不同线程的调用中返回了相同的数值。

解决方法:
        1、在声明方法getNextValue()加上锁,即public synchronized int getNextValue()
        2、采用原子变量类,如:

public class Atomicity {  private AtomicInteger value = new AtomicInteger(0);  public int getNextValue() {  return value.incrementAndGet();  }  
}  

可见性:是关于在哪些情况下,一个线程执行的结果对另一个线程是可见的问题。比如一个共享变量被加载到工作内存中并修改,可见性要求这个修改是对所有线程可见,比如在A线程中 一个变量更新了,那么这种更新就会反映到其他线程中。通过volatile修饰可以实现。

有序性:一个运算赋值操作并不是一个原子性操作,多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性

6、 同步机制

介绍volatile、synchronized和final

一个线程执行互斥代码过程如下:

  1. 获得同步锁;
  2. 清空工作内存;
  3. 从主内存拷贝对象副本到工作内存;
  4.  执行代码(计算或者输出等);
  5. 刷新主内存数据;
  6. 释放同步锁。

所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

volatile是第二种Java多线程同步的手段,根据JLS的说法,一个变量可以被volatile修饰,在这种情况下内存模型确保所有线程可以看到一致的变量值

class Test {    static volatile int i = 0, j = 0;    static void one() {    i++;    j++;    }    static void two() {    System.out.println("i=" + i + " j=" + j);    }    
}   

加上volatile可以将共享变量i和j的改变直接响应到主内存中,这样保证了i和j的值可以保持一致,然而我们不能保证执行two方法的线程是在i和j执行到什么程度获取到的,所以volatile可以保证内存可见性,不能保证并发有序性

如果没有volatile,则代码执行过程如下:

  1.将变量i从主内存拷贝到工作内存;

  2.刷新主内存数据;

  3.改变i的值;

  4.将变量j从主内存拷贝到工作内存;

  5.刷新主内存数据;

  6.改变j的值;

转载于:https://www.cnblogs.com/maydow/p/4873656.html

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

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

相关文章

PyTorch框架学习一——PyTorch的安装(CPU版本)

PyTorch框架学习一——PyTorch的安装(CPU版本)PyTorch简介PyTorch的安装(CPU版)机器学习/深度学习领域的学习都是需要理论和实践相结合的,而它们的实践都需要借助于一个框架来实现,PyTorch在学术界目前处于…

你的大脑在自动驾驶,而你一无所知

来源:果壳想象一下,你的大脑里有着两个小人,它们在不停地打架,试图夺取控制你行动的权力。当然,这两个小人并不是你善意和邪恶的念头。那它们是什么?在很多人看来,这两者就是我们的意识和无意识…

常用的损失函数

来自 机器学习成长之路公众号 本文将常用的损失函数分为了两大类:分类和回归。然后又分别对这两类进行了细分和讲解,其中回归中包含了一种不太常见的损失函数:平均偏差误差,可以用来确定模型中存在正偏差还是负偏差。 从学习任务…

吴恩达《机器学习》学习笔记十一——应用机器学习的建议

吴恩达《机器学习》学习笔记十一——应用机器学习的建议一、训练完模型后下一步要做什么二、评估算法与模型选择1.训练集与测试集2.训练/测试步骤3.模型选择4.数据集新的划分——验证集的加入三、偏差与方差1.偏差与方差的理解2.正则化和偏差方差的关系3.学习曲线四、决定接下来…

为什么说深耕AI领域绕不开知识图谱?

来源:AI科技大本营“所有在 AI 领域深耕的人,最终都会发现语义鸿沟仍是一个非常具有挑战性的问题,这最终还需要借助知识图谱等技术,来帮助将整体的 AI 认知取得新进展。”在 5 月 26 日的 CTA 峰会机器学习专场,Hulu 首…

机器学习中的相似性度量总结

来自 机器学习算法那些事公众号 在做分类时常常需要估算不同样本之间的相似性度量(Similarity Measurement),这时通常采用的方法就是计算样本间的“距离”(Distance)。采用什么样的方法计算距离是很讲究,甚至关系到分类的正确与否。 目录 1. 欧氏距离 …

吴恩达《机器学习》学习笔记十二——机器学习系统

吴恩达《机器学习》学习笔记十二——机器学习系统一、设计机器学习系统的思想1.快速实现绘制学习曲线——寻找重点优化的方向2.误差分析3.数值估计二、偏斜类问题(类别不均衡)三、查准率P与召回率R——代替准确率的评估指标四、查准率与召回率的权衡——…

增强现实:一场正在到来的医疗革命

来源: 资本实验室图像化可以让医生的诊断、决策和治疗更加准确,可以说是医疗史上非常重要的一项技术突破。近几年,通讯技术的发展推动了空间计算的快速商业化。在医疗领域,增强现实(AR)、虚拟现实&#xff…

吴恩达《机器学习》学习笔记十三——机器学习系统(补充)

这次笔记是对笔记十二的补充,之前讨论了评价指标,这次主要是补充机器学习系统设计中另一个重要的方面,用来训练的数据有多少的问题。 笔记十二地址:https://blog.csdn.net/qq_40467656/article/details/107602209 之前曾说过不要…

全球CMOS图像传感器厂商最新排名:黑马杀出

来源:半导体行业观察近期,台湾地区的Yuanta Research发布报告,介绍了其对CMOS图像传感器(CIS)市场的看法,以及到2022年的前景预期。从该研究报告可以看出,2018年全球CMOS图像传感器的市场规模为137亿美元,其…

吴恩达《机器学习》学习笔记十四——应用机器学习的建议实现一个机器学习模型的改进

吴恩达《机器学习》学习笔记十四——应用机器学习的建议实现一个机器学习模型的改进一、任务介绍二、代码实现1.准备数据2.代价函数3.梯度计算4.带有正则化的代价函数和梯度计算5.拟合数据6.创建多项式特征7.准备多项式回归数据8.绘制学习曲线𝜆0𝜆1&…

刘锋 吕乃基:互联网中心化与去中心化之争

前言:本文发表在2019年5月《中国社会科学报》上,主要从神经学角度分析互联网的发育过程,并对云计算和区块链为代表的中心化与去中心化技术趋势进行了探讨。当前,学术界和产业界对互联网的未来发展出现了分歧。随着谷歌、亚马逊、F…

iOS-BMK标注覆盖物

在iOS开发中,地图算是一个比较重要的模块。我们常用的地图有高德地图,百度地图,谷歌地图,对于中国而言,苹果公司已经不再使用谷歌地图,官方使用的是高德地图。下面将讲述一下百度地图开发过程中的一些小的知…

PyTorch框架学习二——基本数据结构(张量)

PyTorch框架学习二——基本数据结构(张量)一、什么是张量?二、Tensor与Variable(PyTorch中)1.Variable2.Tensor三、Tensor的创建1.直接创建Tensor(1)torch.tensor()(2)to…

十年空缺一朝回归,百度正式任命王海峰出任CTO

来源:机器之心百度要回归技术初心了吗?自 2010 年李一男卸任百度 CTO 之后,百度对这一职位就再无公开任命,一空就是 10 年。而今天上午李彦宏突然发出的一纸职位调令,让这个空缺多年的百度 CTO 之位有了新的掌舵手。就…

Windows下卸载TensorFlow

激活tensorflow:activate tensorflow输入:pip uninstall tensorflowProceed(y/n):y如果是gpu版本: 激活tensorflow:activate tensorflow-gpu输入:pip uninstall tensorflow-gpuProceed&#xf…

PyTorch框架学习三——张量操作

PyTorch框架学习三——张量操作一、拼接1.torch.cat()2.torch.stack()二、切分1.torch.chunk()2.torch.split()三、索引1.torch.index_select()2.torch.masked_select()四、变换1.torch.reshape()2.torch.transpace()3.torch.t()4.torch.squeeze()5.torch.unsqueeze()一、拼接 …

'chcp' 不是内部或外部命令,也不是可运行的程序

在cmd窗口中输入activate tensorflow时报错chcp 不是内部或外部命令,也不是可运行的程序 添加两个环境变量即可解决: 将Anaconda的安装地址添加到环境变量“PATH”,如果没有可以新建一个,我的安装地址是“D:\Anaconda”&#xf…

2019年全球企业人工智能发展现状分析报告

来源:199IT互联网数据中心《悬而未决的AI竞赛——全球企业人工智能发展现状》由德勤洞察发布,德勤中国科技、传媒和电信行业编译。为了解全球范围内的企业在应用人工智能技术方面的情况以及所取得的成效,德勤于2018年第三季度针对早期人工智能…

PyTorch框架学习四——计算图与动态图机制

PyTorch框架学习四——计算图与动态图机制一、计算图二、动态图与静态图三、torch.autograd1.torch.autograd.backward()2.torch.autograd.grad()3.autograd小贴士4.代码演示理解(1)构建计算图并反向求导:(2)grad_tens…