在了解Java的内存模型之前先了解下计算机处理并发的模型处理:

由于计算的处理器的处理速度与存储设备的读写速度的差异较大,所以加入一层读写接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲,为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly及Dragon Protocol等。除了增加高速缓存之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)。

如图:

而Java内存模型(Java Main Memory简称JMM)是一个抽象概念,和计算机的内存模型有很多相似的地方。JMM主要包扩线程、:工作内存、主内存三者来交互,其中工作内存可以类比计算的高速缓存,不过线程间工作内存是互相独立的;主内存类比计算机的主内存,线程间变量值传递主要是通过主内存来完成的。同时JMM也有优化代码执行顺序的指令重排序。简单的说就是代码的编写顺序不一定就是代码的执行顺序。

Java内存模型主要定义了程序中的各个变量访问规则,即在虚拟机中将变量存储到主内存和从主内存中取出变量这样的底层细节。此处的变量(Variables)与编程过程的中所说的变量要区分开,它包括了实例字段、静态字段和构成数组对象的元素,不包括编码中的局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不会存在竞争问题。

内存模型的运行态:

通过工作内存、主内存、线程三者的关系,主内存主要是存储变量,线程间变量的传递,工作内存主要负责缓存了存储变量的副本,对变量进行读取,运算,赋值,最后把变量刷新的主内存。

注意: 

  • Java内存模型规定了所有的变量都存储在主内存(Main Memory)中
  • 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。线程和线程之间是没有影响的

那么内存间是如何交户的呢?

主要包括8个步骤:

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

规则说明:

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

这8种内存访问操作以及上述规则限定,再加上稍后介绍的对volatile的一些特殊规定,就已经完全确定了Java程序中哪些内存访问操作在并发下是安全的。

了解了内存模型的三者的对应关系,再来看看Java内存模型的三大特性:

通过Java内存模型的结构图和规则说明中,我们可以看出Java内存说明主要是围绕在并发过程如何处理原子性、可见性和有序性来建立的。这三大特性到底有什么特点呢?

  • 一、原子性(Atomicity):原子代表不可切割的最小单位。原子性是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。利用事务中的原子性举例说明:比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。

再来看看下面的例子:

1、a=1;

2、a=b;

3、i++;

说明:

  • 1.中只有赋值的动作,具体有原子性
  • 2.中b有读取,a有赋值的动作,b是一个变量,如果此时有其他线程修改b的值,那么这个操作的是不具有原子性的。
  • 3.中有对i进行读取,计算,写入的操作,在多线程情况,i的最终值可能不是你想要的,因为其原子遭到破坏。

上面的例子我们从另一个角度分析可以知道原子性涉及到,一个线程执行一个复合操作的时候,其他线程是否能够看到中间的状态、或进行干扰。这也是判断是否符合具体操作是否符合原子性操作的一种思路。

在Java内存模型中来直接保证的原子性变量操作包括read、load、assign、use、store和write,我们大致可以认为基本数据类型的访问读写是具备原子性的。多个操作的话就需要具体分析了。

那么问题来了如何保证原子性操作,Java内存模型也提供lock和unlock两个操作,尽管虚拟机未把lock和unlock操作直接开放给用户使用,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作,这两个字节码指令反映到Java代码中就是同步块——synchronized关键字,因此在synchronized块之间的操作也具备原子性。

在Java中提供了多种原子性保障措施,这里主要涉及三种:

  • 通过synchronized关键字定义同步代码块或者同步方法保障原子性。
  • 通过Lock接口保障原子性。
  • 通过Atomic类型保障原子性。
  • 二、可见性:可见性(Visibility):可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的。

三种保证可见性的操作:

  • volatile:volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
  • synchronized:synchronized关键字在释放锁之前,必须先把此变量同步回主内存中(执行store、write操作)。
  • final:被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见final字段的值。简单的说就是final修饰的变量,一旦完成初始化,就不能改变。
  • 三、有序性:有序性主要涉及了指令重排序现象和“工作内存与主内存同步延迟”现象。总结为一句话:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。
  • 如何保证有序性:Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile关键字本身就包含了禁止指令重排序的语义,而synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这条规则决定了持有同一个锁的两个同步块只能串行地进入。

Java内存模型及三大特性相关推荐

  1. JMM内存模型 多线程三大特性

    本文目录 1.JMM 内存模型 1.主内存 2.工作内存 3.JMM 线程操作内存的两条基本规定 4.JMM 模型八大内存交互指令 5.JMM 模型内存交互操作 6.JMM 模型内存同步规则 2.多线 ...

  2. java内存模型按照线程隔离性_深入理解Java多线程与并发框(第③篇)——Java内存模型与原子性、可见性、有序性...

    一.Java内存模型 Java Memory Modle,简称 JMM,中文名称 Java内存模型,它是一个抽象的概念,用来描述或者规范访问内存变量的方式.因为各中计算机的操作系统和硬件不同,方式机制 ...

  3. java 线程 原子性_深入理解Java多线程与并发框架——Java内存模型与原子性、可见性、有序性...

    欢迎关注专栏<Java架构筑基>--专注于Java技术的研究与分享!Java架构筑基​zhuanlan.zhihu.comJava架构筑基--专注于Java技术的研究与分享! 后续文章将首 ...

  4. Java多线程编程笔记4:Java内存模型

    Java内存模型 Java内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果. 处理器上的寄存器读写速度比内存快几个数量级,为了解决这种速度矛 ...

  5. JAVA内存模型与线程安全

    什么是线程安全?在<<深入理解Java虚拟机>>中看到的定义.原文如下:当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替运行,也不需要进行额外的同步, ...

  6. java 内存模型 多线程_Java 高并发三:Java内存模型和线程安全详解

    网上很多资料在描述Java内存模型的时候,都会介绍有一个主存,然后每个工作线程有自己的工作内存.数据在主存中会有一份,在工作内存中也有一份.工作内存和主存之间会有各种原子操作去进行同步. 下图来源于这 ...

  7. Java内存模型三大特性

    文章目录 一.原子性 如何保证原子性? 1.使用原子类来保证线程安全 2.使用synchronized 互斥锁 二.可见性 如何保证可见性? 三.有序性 如何保证有序性? 1.volatile关键字 ...

  8. Java并发知识梳理(上):并发优缺点,线程状态转换,Java内存模型,Synchronized,Volatile,final,并发三特性,Lock与AQS,ReetrandLock

    努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 整个系列文章为Java并发专题,一是自己的兴趣,二是,这部分在实际理解上很有难度,另外在面试过程中也是经常被问到.所以在学习过程中,记 ...

  9. java(线程特性,Volatile作用,java内存模型)

    1.java线程特性   1.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行 银行转账,自己转100给别人,自己账户少100 别人多100 不会出现,自己 ...

最新文章

  1. 在OSX上安装Erlang
  2. C#中的SMTP配置Outlook.Com SMTP主机
  3. 15年第六届蓝桥杯第九题_(矩阵快速幂优化的动态规划)
  4. DCMTK:测试DcmSCU的C-FIND / GET / MOVE会话处理
  5. Apache http强制转为https页面访问(转)
  6. C# WPF DataGrid获取单元格并改变背景色
  7. 支架预压弹性变形值计算_现浇连续梁桥支架搭设及预压施工技术控制
  8. 属兔的人有什么缺点呢?
  9. 请投量子位一票 | 2018年度明星学术公众号评选
  10. 剑指|| offer1整数除法
  11. 2022百度之星程序设计大赛 - 复赛 1003 最大值
  12. Python 调用摄像头进行人脸识别
  13. matlab fisher z变换,科学网—胜过 Fisher z 变换!(2) - 杨正瓴的博文
  14. drwxr-xr-x是啥意思
  15. 坑爹的AWS免费服务
  16. 软件测试的过程和产生的文档
  17. 世纪金文oracle,21世纪双语新闻(MP3+字幕):一片甲骨天下惊,甲骨文的意义何在?...
  18. 盘点史上最危险的10种创业合伙人
  19. uniapp开发微信小程序弹窗自用封装
  20. MP4/MOV/3GP文件的“ftyp”

热门文章

  1. 游戏编程这些年的苦与乐
  2. 加密数字艺术背后你关心的几个问题
  3. Minecraft我的世界服务器配置5人/10人/50人玩家搭建mc服务器
  4. python入门教程百度云-Python爬虫入门教程 百度云盘下载【传智播客】
  5. 电脑电源输出电压的色线代表多少伏
  6. 计算机应用2010综合测试8,计算机应用基础excel2010综合测试操作步骤参考自测题步骤.docx...
  7. 16电口8个百兆SFP插糟+4个千兆SPF插糟(光电可选 )工业交换机
  8. 论文笔记:SPADE(CVPR 2019)-Semantic Image Synthesis with Spatially-Adaptive Normalization
  9. pythonos模块使用教程_Python os模块详解
  10. (病理图像读写)病理图像(whole slide images,WSI)的读写(.svs, .tiff),使用openslide,和pyvips以及matlab