Java并发编程之Java内存模型

目录
  • 1、什么是Java的内存模型
  • 2、为什么需要Java内存模型
  • 3、Java内存模型及操作规范
  • 4、Java内存模型规定的原子操作
  • 5、Java内存模型同步协议
  • 6、Java内存模型的HB法则
    • JMM的HB法则
  • 总结

1、什么是Java的内存模型

Java内存模型简称JMM(Java Memory Model),JMM是和多线程并发相关的一组规范。各个jvm实现都要遵循这个JMM规范。才能保证Java代码在不同虚拟机顺利运行。因此,JMM 与处理器、缓存、并发、编译器有关。它解决了CPU 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。

2、为什么需要Java内存模型

程序的运行结果依赖于处理器,而不同的处理器规则都不一样,不同处理器差异是很大的,所以同段代码在处理器A运行正常,搬到处理器B运行结果是不一样的,所以为了兼容这种差异,推出了Java内存模型规范,JMM是一个规范标准,JMM保证了不同处理器的处理结果一致,同时也保证不同编译器、jvm等等的一致性。所以就保证了Java语言“书写一次、到处运行”

3、Java内存模型及操作规范

1.共享变量都是放在主内存中的

2.每个线程都有自己的工作内存,线程只可操作自己的工作内存

3.线程要操作共享变量,需要从主内存中读取到工作内存,改变值之后要从工作内存同步到主内存

4、Java内存模型规定的原子操作

Java内存模型的同步交换协议,规定了8种原子操作

原子操作:不可被中断的一个或一系列操作

  • lock(锁定):将主内存中的变量锁定,为一个线程所独占
  • unlock(解锁):将lock加的锁解除,其他的线程有机会访问此变量
  • read(读取):作用于主内存变量,将主内存中的变量值读取到工作内存
  • load(加载):作用于工作内存,将read读取到的值保存到工作内存中的变量副本
  • use(使用):作用于工作内存变量,将值传递给线程的代码执行引擎
  • assign(赋值):作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本
  • store(存储):作用于工作内存变量,将变量副本的值传送到主内存中
  • write(写入):作用于主内存变量,将store传送过来的值写入到主内存的共享变量中

Java内存模型的同步交互协议,执行上述8种原子操作时必须满足如下规则

不允许read和load,store和write操作之一单独出现。即不允许加载或同步工作到一半。

不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变之后,必须将数据同步回主内存

不允许一个线程无原因地(无assign操作)将数据从工作内存同步到主内存中。

一个新的变量可能在主内存中诞生。

一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次lock之后必须要执行相同次数unlock操作,变量才会解锁

如果对一个对象进行lock操作,那么会清空工作内存变量中的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始变量的值

如果一个对象事先没有被lock,就不允许对其进行unlock操作,也不允许去unlock一个被其他线程锁住的变量。

对一个变量执行unlock操作之前,必须将此变量同步回主内存中(执行store、write)

5、Java内存模型同步协议

Java内存模型的同步协议,操作规范 将一个变量从主内存复制到工作内存要顺序执行read、load操作;要将变量从工作内存同步回主内存要用store、write操作。只要求顺序执行,不一定是连续执行

图引用网上资料:

6、Java内存模型的HB法则

并发编程有三个重要特效:原子行可见性有序性

  • 原子性:原子性是指一个或者多个操作,要么全部执行且执行过程不会被其它操作打断,要么全部不执行。
  • 可见性:可见性是指共享变量对于多个线程都是可见的,也即一个线程修改了变量,其它线程马上就能知道
  • 有序性:有序性是指程序的执行顺序按照代码的先后顺便执行

在说JMM的happens-before(HB)法则之前,先说说并发编程的有序性。说到并发线程的有序性,还需要涉及到指令重排序

  • 什么是指令重排?

假如我们写一个程序,我们会期待这些语句的实际执行顺便和代码的顺序是一致的,大部分情况是一致的,但实际上,编译器、JVM 或者 CPU 都有可能出于优化等目的,对执行的顺序进行调整,这个就是指令重排序

  • 重排序的好处:提高处理速度

代码顺序如图:

指令重排后,a=100; a= a+100会提到一起执行,效率提高

上面的例子,是可以提高执行效率,但是有时候指令重排是会导致问题的,如下代码例子,代码顺序是先初始化content,然后设置标识为true,线程B检测到为true之后,调用content的方法

如果指令重排后,这种情况就会出现没初始化完成,就直接调用conten的方法

所以,指令重排有好处也有坏处,一般可能是cpu、编译器或者是内存会进行指令重排,为了避免指令重排,保证并发编程的有序性,有时候需要使用synchronized等锁或者volatile等等方式避免

1.JMM规定了happens-before(先行发生)原则,来保证很多操作的有序性。

2.当我们代码操作不满足先行发生原则时,则需在编码时使用volatile、synchronized来保证有序性

JMM的HB法则

  • 程序顺序规则:每个线程的每个操作都happens-before该线程中任意的后续操作
  • 监视器锁规则:一个锁的解除,happens-before于随后对这个锁的加锁
  • volatile变量规则:对volatile域的写,happens-before于任意后续对这个volatile域的读
  • 线程启动规则:在某个线程对象上调用start()方法happens-before被启动线程中的任意动作
  • 线程终止规则:线程中所有操作都先行发生于对此线程的终止检测,如在线程t1中成功执行了t2.join(),则t2中的所有操作对t2可见
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize方法的开始传
  • 递性:如果A happens-before于B,且B happens-before 于C,那么A happens-before于C

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 并发编程之Java内存模型volatile的内存语义

    1.volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对单个读/写操作做了同步. 代码示例: package com.lizba.p1; /** * <p> * volatile示例 * </p> * * @Author: Liziba * @Date: 2021/6/9 21:34 */ public class VolatileFeatureExample { /** 使用volatile声明64位的long型

  • Java并发编程之内存模型

    目录 一.Java内存模型的基础 1.1 并发编程模型的两个关键问题 1.2 Java内存模型的抽象结构 1.3 从源代码到指令重排序 1.4 写缓冲区和内存屏障 1.4.1 写缓冲区 1.4.2 内存屏障 1.5 happens-before 简介 简介: Java线程之间的通信对程序员完全透明,内存可见性问题很容易困扰Java程序员,这一系列几篇文章将揭开Java内存模型的神秘面纱. 这一系列的文章大致分4个部分,分别是: Java内存模型基础,主要介绍内存模型相关基本概念 Java内存模型

  • 并发编程之Java内存模型顺序一致性

    目录 1.数据竞争和顺序一致性 1.1 Java内存模型规范对数据竞争的定义 1.2 JMM对多线程程序的内存一致性做的保证 2.顺序一致性内存模型 2.1 特性 2.2 举例说明顺序一致性模型 2.3 同步程序的顺序一致性效果 2.4 未同步程序的执行特性 3. 64位long型和double型变量写原子性 3.1 CPU.内存和总线简述 3.2 long和double类型的操作 简介: 顺序一致性内存模型是一个理论参考模型,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参照

  • 并发编程之Java内存模型锁的内存语义

    目录 1.锁的释放-获取建立的happens-before关系 2.锁释放和获取的内存语义 3.锁内存的语义实现 4.concurrent包的实现 简介: 锁的作用是让临界区互斥执行.本文阐述所得另一个重要知识点--锁的内存语义. 1.锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 锁释放-获取的示例代码: package com.lizba.p1; /** * <p>

  • Java并发编程之Java内存模型

    目录 1.什么是Java的内存模型 2.为什么需要Java内存模型 3.Java内存模型及操作规范 4.Java内存模型规定的原子操作 5.Java内存模型同步协议 6.Java内存模型的HB法则 JMM的HB法则 总结 1.什么是Java的内存模型 Java内存模型简称JMM(Java Memory Model),JMM是和多线程并发相关的一组规范.各个jvm实现都要遵循这个JMM规范.才能保证Java代码在不同虚拟机顺利运行.因此,JMM 与处理器.缓存.并发.编译器有关.它解决了CPU 多

  • Java并发编程之Volatile变量详解分析

    目录 一.volatile变量的特性 1.1.保证可见性,不保证原子性 1.2.禁止指令重排 二.内存屏障 三.happens-before Volatile关键字是Java提供的一种轻量级的同步机制.Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量, 相比synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度. 但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其

  • 浅谈Java并发编程之Lock锁和条件变量

    简单使用Lock锁 Java 5中引入了新的锁机制--java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接口有3个实现它的类:ReentrantLock.ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁.读锁和写锁.lock必须被显式地创建.锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例

  • 深入分析Java并发编程之CAS

    在Java并发编程的世界里,synchronized 和 Lock 是控制多线程并发环境下对共享资源同步访问的两大手段.其中 Lock 是 JDK 层面的锁机制,是轻量级锁,底层使用大量的自旋+CAS操作实现的. 学习并发推荐<Java并发编程的艺术> 那什么是CAS呢?CAS,compare and swap,即比较并交换,什么是比较并交换呢?在Lock锁的理念中,采用的是一种乐观锁的形式,即多线程去修改共享资源时,不是在修改之前就加锁,而是乐观的认为没有别的线程和自己争锁,就是通过CAS的

  • Java并发编程之LockSupport类详解

    一.LockSupport类的属性 private static final sun.misc.Unsafe UNSAFE; // 表示内存偏移地址 private static final long parkBlockerOffset; // 表示内存偏移地址 private static final long SEED; // 表示内存偏移地址 private static final long PROBE; // 表示内存偏移地址 private static final long SEC

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

  • Java并发编程之ThreadLocal详解

    目录 一.什么是ThreadLocal? 二.ThreadLocal的使用场景 三.如何使用ThreadLocal 四.数据库连接时的使用 五.ThreadLocal工作原理 六.小结 七.注意点 一.什么是ThreadLocal? ThreadLocal叫做线程本地变量,ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的.ThreadLocal为变量在每个线程中都创建了一个副本,则每个线程都可以访问自己内部的副本变量. 二.ThreadLocal的使用场景 1.当对象

随机推荐