当前位置: 首页 > news >正文

【JVM】详解 Java内存模型(JMM)

Java内存模型(Java Memory Model,JMM)

Java内存模型(Java Memory Model,JMM)是用来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。

Java内存模型的主要目的是定义程序中各种变量的访问规则即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节

对于变量的存储与操作,JMM规定了主内存((Main Memory)和工作内存(Working Memory,与处理器高速缓存类比)​两种内存:

        Java内存模型规定了所有的变量都存储在主内存中。每条线程还有自己的工作内存​,线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的数据

JVM定义了八种原子性的操作:

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

volatile关键字

当一个变量被定义成volatile之后,它将具备两项特性:

  • 第一项是保证此变量对所有线程的可见性。这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
  • 第二个特性是禁止指令重排序优化。普通的变量仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。

volatile关键字在Java中主要通过内存屏障来禁止特定类型的指令重排序。

1)写-写(Write-Write)屏障

        在对volatile变量执行写操作之前,会插入一个写屏障。这确保了在该变量写操作之前的所有普通写操作都已完成,防止了这些写操作被移到volatile写操作之后。

2)读-写(Read-Write)屏障

        在对volatile变量执行读操作之后,会插入一个读屏障。它确保了对volatile变量的读操作之后的所有普通读操作都不会被提前到volatile读之前执行,保证了读取到的数据是最新的。

3)写-读(Write-Read)屏障

        这是最重要的一个屏障,它发生在volatile写之后和volatile读之前。这个屏障确保了volatile写操作之前的所有内存操作(包括写操作)都不会被重排序到volatile读之后,同时也确保了volatile读操作之后的所有内存操作(包括读操作)都不会被重排序到volatile写之前

原子性、有序性、可见性

1)原子性

Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个,可以大致认为,基本数据类型的访问、读写都是原子性的。

2)有序性

Java程序中的有序性可以总结为一句话:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。

3)可见性

可见性就是指当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。除了volatile之外,Java还有两个关键字能实现可见性,它们是synchronized和final。synchronized实现可见性的原因是unlock之前必须将执行的结果同步到主内存中。

Happens-Before

它是判断数据是否存在竞争,线程是否安全的非常有用的手段。依赖这个原则,我们可以通过几条简单规则一揽子解决并发环境下两个操作之间是否可能存在冲突的所有问题,而不需要陷入Java内存模型苦涩难懂的定义之中。

程序次序规则(Program Order Rule)在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。

管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是“同一个锁”​,而“后面”是指时间上的先后。

volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后。

线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。

线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread::join()方法是否结束、Thread::isAlive()的返回值等手段检测线程是否已经终止执行。

线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread::interrupted()方法检测到是否有中断发生。

对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

注意

时间先后顺序与先行发生原则之间基本没有因果关系,所以我们衡量并发安全问题的时候不要受时间顺序的干扰,一切必须以先行发生原则为准!

时间上的先行发生不代表这个操作一定是先行执行的,反之也不成立。

private int value = 0;pubilc void setValue(int value){this.value = value;
}public int getValue(){return value;
}

假设线程A先调用setValue(1)方法,线程B调用getValue()方法,线程B是否能得到理想的结果呢?

由于不是在同一个线程内,程序次序规则是不适用的,将上述所有的规则带入也发现没有一个成立的,因此是无法确定线程B的执行结果的,也就是说这是线程不安全的。

http://www.dtcms.com/a/560562.html

相关文章:

  • 做网站工作室广告网站建设
  • 小语种网站制作广州网站建设哪里有
  • 广州学做网站上饶网站建设多少钱
  • GO写的http服务,清空cookie
  • 响应式企业网站模板望京网站建设公司
  • 最新聊天记录做图网站ip软件点击百度竞价推广
  • 关于学校网站建设申请报告深圳市网络seo推广价格
  • 公司网站后台怎么上传图片百度西安分公司地址
  • Go语言设计模式:组合模式详解
  • 南昌市住房和城乡建设网站检测网站是否正常
  • 自建网站费用营销推广的主要方法
  • 罗田做网站一个人看的在线观看视频免费下载
  • 网站 首页 栏目 内容wordpress发文章
  • 云南建设厅和网站一个软件的制作过程
  • 湛江网站制作系统厦门哪些企业做视频网站的
  • 宝塔做网站安全吗网站建设课程设计要求
  • 建设微信网站制作自建购物网站
  • 网站的建设和推广上传网站教程
  • 怎么做网站关键字搜索超办网ppt下载
  • 杨凯做网站软件开发平台哪家好
  • 各大搜索引擎提交网站入口大全动漫网页设计作品模板
  • 优化大师怎么样seo优化一般多少钱
  • 设计师个人网站欣赏湖南企业竞价优化
  • 外贸网站建设wordpress的ping列表
  • 昆山高端网站建设咨询统一开发平台
  • 做网站要用什么语言网站建设干货图书
  • 加密网站wordpress 字体大小
  • 网站建设标志图如果在各大网站做免费的网络推广
  • 运城个人网站建设织梦建设网站全过程
  • 网站建设 大公司好网站教程设计