JVM之内存模型

前言

每一个线程有一个工作内存。工作内存和主存独立。工作内存存放主存中变量的值的拷贝。


image
  • 当数据从主内存复制到工作存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;
  • 当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作。

每一个操作都是原子的,即执行期间不会被中断

对于普通变量,一个线程中更新的值,不能马上反应在其他变量中。如果需要在其他线程中立即可见,需要使用volatile关键字作为标识。

image

原子性、可见性、有序性

原子性

由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write。我们大致可以认为基本数据类型的访问读写是具备原子性的。
如果应用场景需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求,尽管虚拟机没有把lock和unlock操作直接开放给用户,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作,这两个字节码指令反映到Java代码中就是同步块---synchronized关键字,因此在synchronized块之间的操作也具备原子性。

可见性

一个线程修改了变量,其他线程可以立即知道。

保证可见性的方法:

  • volatile:保证新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
  • synchronized (unlock之前,写变量值回主存)
  • final(一旦初始化完成,其他线程就可见)

有序性

在本线程内,操作都是有序的;
在线程外观察,操作都是无序的。(指令重排 或 主内存同步延时)

指令重排

image

指令重排破坏了线程间的有序性


image

保证有序性方法:

  • volatile :禁止指令重排序
  • synchronized :一个变量在同一时刻只允许一条线程对其进行lock操作。

内存屏障(memory barriers)

内存屏障的作用 :

  • 阻止屏障两侧的指令重排序
  • 强制刷新主内存数据,以及让缓存中相应的数据失效。
    Java的内存屏障有的四种,LoadLoad,StoreStore,LoadStore,StoreLoad


先行发生原则

程序顺序原则:一个线程内保证语义的串行性.对于单线程来讲,必须保证重排后的结果与重排前一致。
volatile规则:volatile变量的写,先发生于后续对这个变量的读.这保证了volatile变量的可见性.
监视锁规则:对于一个锁的解锁,先发生于随后对这个锁的加锁. 否则随后的加锁将会失败.
传递性:A先于B,B等于C,那么A必然先于C.
线程启动规则:Thread对象的start()方法先发生于此线程的其他任意动作。
线程终止规则:线程的所有操作都先发生于对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
线程中断规则:对线程interrupt()方法的调用先发生于被中断线程的代码检测到中断时事件的操作。
对象终结规则:一个对象的初始化完成(构造函数执行结束)先发生于它的finalize()方法的开始

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁...
    架构师springboot阅读 5,753评论 0 5
  • 本篇将介绍虚拟机如何实现多线程、多线程之间由于共享和竞争数据而导致的一系列问题及解决方案。 概述 Java内存模型...
    厘米姑娘阅读 11,155评论 7 43
  • 背景:看完《深入理解Java虚拟机》和相关博客,对JVM还是没有一个条理清晰的认识,遂提取了书中相关知识点和参考相...
    LandHu阅读 4,822评论 0 1
  • 除了充分利用计算机处理器的能力外,一个服务端同时对多个客户端提供服务则是另一个更具体的并发应用场景。衡量一个服务性...
    胡二囧阅读 5,180评论 0 12
  • 目录: 1. 指令重排 2. 顺序一致性 3. volatile 4. final 1.指令重排 要了解指令重排,...
    西部小笼包阅读 4,153评论 0 1