Java内存模型读书笔记-基础

基本概念

并发需要处理的两个问题

同步通信

  • 通信的机制:共享内存(读写内存中的状态来隐性通信,JAVA中使用该方式)和消息传递(通过明确发消息来显式进行通信)
  • 同步:用于控制不同线程之前操作发生的顺序的机制。在Java中,可以通过volatile,synchronized,锁等方式实现同步。

在共享内存的通信机制,必须指定某段代码在线程之间互斥执行,同步是显式进行的;消息传递的通信机制中则是隐式

JMM

JAVA线程之间通信由JAVA内存模型控制,决定线程共享的变量的写入合适对另外一个线程可见

重排序

  • 目的:提高性能
  • 分类:编译器重排序(编译优化重排序),处理器重排序(指令集并行重排序,内存系统重排序)

都可能带来内存可见性问题,所以JMM禁止某些编译器重排序;对于处理器重排序则通过内存屏障指令。

处理器重排序

现代处理器使用写缓冲区的方式来临时保存向内存写数据,如下:

  • 好处是:
    • 对统一内存地址读写批处理,减少内存总线的占用
    • 保证指令流水线持续运行,不需要每次都停顿下来写内存
  • 带来的问题:

    • 对内存的执行顺序产生重要的影响,写缓冲区仅仅对自己的处理器可见:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写顺序一致!!!

    关于重排序,我们需要理解它的思想:为了提高程序的并发度,从而提高性能!但是对于多线程程序,重排序可能会导致程序执行的结果不是我们需要的结果!因此,就需要我们通过volatilesynchronize,锁等方式作出正确的实现同步

数据依赖性

定义:如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性(写后读,写后写,读后写)
作用:编译器和处理器不会对“存在数据依赖关系的两个操作”执行重排序

注意:仅针对单线程,单处理器

as-if-serial语义

定义:不管怎么重排序,(单线程)程序的执行结果不能被改变

happen-before

happen-before用来阐述操作之间的内存可见性,如果一个操作执行的结果需要对另外一个结果可见,那么两个操作之间必须存在happen-before关系

规则


  • 1.程序顺序规则:一个线程中的每个操作,happen-before于该线程中的任意后续操作。
  • 2.监听器锁规则:对一个监视器锁的解锁,happen-before于随后对这个监视器锁的加锁(即对后续的加锁操作内存可见)
  • 3.volatile变量规则:对一个volatile域的写,happen-before于任意后续对这个volatile域的读(即对后续的读内存可见)
  • 4.传递性

注意:一个操作和另外一个操作存在happen-before关系,并不代表前一个操作必须在后一个操作之前执行!仅要求前一个操作对后一个操作可见(前一个操作的结果对后一个操作来说是可见?),且前一个操作按照顺序排在第二个操作之前(the first is visiable and ordered before the second)
例如

double pi=3.14; //A
double r=1.0 //B
double area=pi*r*r; //C

对于单线程来说,A和C,B和C都存在依赖关系,所以C不能重排序在A和B之前,但A,B五数据依赖性,所以A,B可以重排序,并遵循as-if-serial语义。而根据happen-before的规则1,A happen-before B,B happen-before C,A happen-before C,这里A happen-before B,但B却可以在A之前执行

顺序一致性

数据竞争和顺序一致性保证:

当程序未正确同步执行(同步指广义上的同步,包括常用同步原语,synchronized,volatile,final),就可能存在数据竞争

数据竞争 定义:
在一个线程中写一个变量,在另外一个线程读同一个变量,而且读写没有通过同步来排序

JMM对正确同步的多线程程序的内存一致性做了如下保证:
如果程序是正确同步的,程序的执行将具有顺序一致性。即程序的执行结果和程序在顺序一致性内存模型中的执行结果一致

顺序一致性的内存模型
  • 定义:它是理想化的内存模型,JMM并不提供该保证,所以需要自己使用同步原语来保证同步,因为需要做大量的编译器优化和处理器优化
  • 目的:提供内存可见性的保证
  • 特征:

    • 1.一个线程的所有操作必须按照程序的顺序执行
    • 2.不管程序是否同步,所有线程只能看到一个单一的操作执行顺序。每个操作必须原子执行且立刻对所有线程可见

    在顺序一致性模型(前者)和JMM(后者)中执行的差别:

  • 1.前者保证线程内的所有操作顺序执行,后者不保证(即使在正确同步多线程程序,会进行重排序)
  • 2.前者保证所有线程只能看到一致的操作执行顺序(提供内存可见性的保证),后者不保证。
  • 3.后者不保证对64位的long型和double型变量的读写具有原子性(32位内存总线机器上,64位的long的写操作分为两个总线事务,通过总线仲裁决定当前事务,JDK1.5开始,而读事务是确保了其原子性,每次读64位)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值