聊聊java并发编程[java实现之JMM]

通过本篇文章,你可以从概念上理解java对于编发编程遇到的问题解决的原则或者是思想(JMM).

引言

聊聊java并发编程 [核心问题以及java解决方案]聊聊java并发编程 [java实现的基石] 这两篇文章,我们聊到了并发编程的核心问题,以及java依赖相关工具实现。本篇文章,我们开始深入浅出聊下这些工具。

部分概念

  • 线程之间的同步
    • 同步具体是指程序中不同线程间操作发生的相对顺序
    • java 共享内存模型表示程序员必须显示的对某个方法或代码块互斥执行。是显式的。
  • 线程之间的通信
    • 通信具体是指线程之间交换信息
    • java共享内存模型 表示: 线程间的通信都是隐式通讯的。
  • JVM 部分概念

现存的问题

  • 由于JVM多线程对共享内存区域是有线程不安全的。
  • 所以JVM定义了一组规则(JMM) 来解决这个问题(本质上是内存可见性)。
  • JMM 解决这个问题的有下面几个原则:
    • 原子性
    • 有序性
    • 可见性(主)

JMM相关概念以及理解

  • JMM(Java Memory Model)即 java 内存模型。是一个抽象的概念规范。其约定了程序中的各个变量(包括实例,静态字段以及其他对象数组元素)的访问方式。
  • JMM 主要有主内存,工作内存两个概念,来解决线程间的通信和同步问题。
  • 主要解决内存可见性
    JMM 主内存
  • 线程之间的共享变量(所有实例域,静态域,数组元素,这些都是存储在JVM堆内存中)存储在主内存中。
  • 主要存储Java实例对象。无论该对象是局部变量还是成员变量,自然也包括,共享的类信息,,静态变量。基本可以理解所有的数据都会在主内存中存在。 多个线程同时操作某个变量,就会引发线程不安全。
  • 实际存储:
    • 从JVM的角度来讲,如果对于一个实例对象方法中的本地变量是基础数据类型(boolean,byte,short,char,int,long,float,double),都是直接存储在JVM工作内存的栈帧结构。
    • 如果本地变量是引用类型,则改变量的引用会存储在JVM工作内存的栈帧结构中,对象实例会在JVM主内存中(共享区域,堆). 对于实例的成员变量不管是基础类型还是非基础类型都是存储在堆中的。类本身信息以及静态变量也是存储在JVM主内存中的。
    • 如果两个线程都调用了某个实例的方法,那么两个线程会将要操作的数据copy一份到自己的工作内存中,执行完成之后才会刷新到自己的工作内存中。
      JMM 工作内存
  • 每个线程都有自己的私有的本地内存。本地内存存储了该线程的读/写共享变量的副本。
  • 从JVM角度来讲:JMM内存因为是私有的数据变量,某种程度上可以理解为JVM线程栈(虚拟机栈,本地方法栈,程序计数器)
    JMM 抽象示意图
  • JMM的抽象示意图 (来源于<Java并发编程的艺术>)
  • 举例(例子来源于<Java并发编程的艺术>)
    • 线程A与线程B进行数据同步(线程A先修改某个变量值,然后线程B获取更新后的值。)
    • JMM 解决原子性

  • 除了JVM自身提供的对基本数据类型读写操作的原子性外,对于方法级别或者代码块级别的原子性操作,可以使用synchronized关键字或者锁保证程序执行的原子性。

JMM 解决可见性

  • 使用sychronized 以及volatile 都可以在一个线程对某个变量修改后,其他变量可见。

JMM 解决有序性

  • 有序性涉及到了指令重排(可以简单理解为硬件系统会将程序的指令做一次优化, 优化的时候可能会存在结果不一致的存在)。
  • 这种使用volatile就可以解决。 volatile是禁止重排序优化。

参考大佬(膜拜)