专栏首页Java后端技术栈并发编程JMM系列之基础!

并发编程JMM系列之基础!

来源 | 公众号 | Justin的后端书架

Java程序员在进行多线程开发时,并不需要关心线程间是如何通信的,这些对程序员本来来说完全是透明的,但是内存可见性问题很容易让我们困惑,今天我们就讲讲Java内存模型(JMM)相关知识点,首先我们先讲讲内存模型的一些基本概念,对内存模型有个大概的认识,让我们开始今天的并发之旅吧。

一、并发编程模型的关键问题

并发编程模型主要有两种模型:共享内存模型和消息传递模型,在这两种模型中都需要解决并发编程最关键的两个问题:

共享内存模型:

  • 线程之间通信:线程之间共享程序的公共状态,通过读-写内存中的公共状态,进行隐式通信;
  • 线程之间同步:程序员必须显式的指定某个方法或者代码块需要在线程间互斥执行,显式进行同步;

消息传递模型:

  • 线程之间通信:线程之间通过发送消息,进行显式通信;
  • 线程之间同步:消息的发送必须在消息的接收之前,隐式进行同步;

通信指的是线程之间以什么机制来交换信息;

同步指的是程序用于控制不同线程间操作发生相对顺序的机制;

Java的并发采用的是共享内存模型,Java之间线程通信是隐式进行的,因此我们要理解隐式进行的线程间通信机制,才能更好的掌握Java并发,下面我们就谈谈这种隐式的通信机制。

二、Java内存模型的抽象结构

在Java中,所有的实例域,静态域和数组元素都存储在堆内存中,堆内存在线程间是共享的,因此这些数据对象会受到内存模型的影响;

Java线程之间的通信就依赖于Java内存模型(JMM),JMM决定了一个线程对共享变量的写入何时对另一个线程可见;从抽象角度看,JMM定义了线程和主内存之间的抽象关系,如下图所示:

JMM抽象模型:线程之间的共享变量存在主内存中,每个线程都有一个本地内存,用于存储主内存中共享变量的副本;

JMM线程通信过程:线程A每次把本地内存A中更新过的共享变量刷新到主内存中,线程B到主内存中去读取线程A更新后的共享变量,并刷新到本地内存B中;完成线程AB之间的通信。这个过程一定得经过主内存,所以JMM是通过控制主内存与每个线程的本地内存之间交互来提供内存可见性的保证机制。

三、源代码到指令序列重排序

为了提高程序执行的性能,编译器和处理器常常会对指令做重排序处理,整个过程如下:

四、处理器对内存操作的影响

现在常见的处理器都使用写缓冲区,首先我们看下使用写缓冲区有什么好处?再看看对内存操作有什么影响?

写缓冲区的优势:写缓冲区临时保存线程向内存写入的数据,这样做可以避免处理器停下来等待向内存写入数据产生的延迟,通过批处理的方式刷新写缓冲区,合并写缓冲区中对同一内存地址的多次读写,从而减少了对内存总线的占用,提高了处理器处理数据的效率。

对内存执行顺序的影响:处理器对内存读写操作顺序不一定与内存实际发生的读写顺序一致,可能造成数据结果的不一致。

怎么解决这个问题呢?

还是之前提到的内存屏障,Java编译器在生成指令时会在适当位置插入内存屏障指令来禁止特定类型的处理器重排序(执行顺序会影响执行结果的这种),JMM把内存屏障分为下面四种:

五、先行发生原则

在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作直接必须要存在先行发生原则(happens-before)。

先行发生原则规则如下:

程序次序规则:同一个线程内,按照程序代码顺序,写在前面的操作先行发生于写在后面的操作;

管程锁定原则:对于一个锁的解锁,先行发生于随后对这个锁的加锁;

volatile变量规则:对一个volatile变量的写操作先行发生于任意后续对该变量的读操作;

线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作;

线程终止规则:线程的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束和Thread.isAlive()的返回值等手段检测到线程已经终止执行。

线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断时事件的发生。Thread.interrupted()可以检测是否有中断发生。

对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()的开始。

传递性:如果操作A 先行发生于操作B,操作B 先行发生于操作C,那么可以得出A 先行发生于操作。

本文分享自微信公众号 - Java后端技术(JavaITWork),作者:Justin的后端书架

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-03-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java多线程编程-(5)-线程间通信机制的介绍与使用(温馨提示:图文较多,建议Wiff下打开)

    我们知道线程是操作系统中独立的个体,但是这个单独的个体之间没有一种特殊的处理方式使之成为一个整体,线程之间没有任何交流和沟通的话,他就是一个个单独的个体,不足以...

    Java后端技术
  • 【面试题】2018年最全Java面试通关秘籍第四套!

    注:本文是从众多面试者的面试经验中整理而来,其中不少是本人出的一些题目,网络资源众多,如有雷同,纯属巧合!禁止一切形式的碰瓷行为!未经允许禁止一切形式的转载和复...

    Java后端技术
  • Java多线程编程-(15)- 关于锁优化的几点建议

    在《 Java多线程编程-(11)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力》 这一篇文章中,我们大致介绍了...

    Java后端技术
  • 浅析java内存模型--JMM

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步?

    烂猪皮
  • Java内存模型 - JMM

    Java语言的其中一个特点为跨平台性即由Java编写的程序,一次编译后就可以在多个系统平台上运行。

    虞大大
  • JVM的基础知识点Java的内存模型

    Java虚拟机是Java工程师必学的进阶功课,这段时间开始死磕JVM。今天梳理一下JVM的基础知识点Java的内存模型!

    java乐园
  • java 内存模型的基础(二)

    如果线程A与线程B之间要通信的话,必须要经历下面2个步骤。 1 线程A把本地内存A中更新过的共享变量刷新到主内存中去。 2 线程B到主内存中去读取...

    爱明依
  • 关于Java变量的可见性问题

    itliusir
  • Java多线程-带你认识Java内存模型,内存分区,从原理剖析Volatile关键字

    地址:https://juejin.im/post/59f8231a5188252946503294

    用户2802329
  • 什么是Java内存模型

    有一个关于JVM名词定义的问题,说”JVM内存模型“,有人会说是关于JVM内存分布(堆栈,方法区等)这些介绍,也有地方说(深入理解JVM虚拟机)上说Java内存...

    java架构师

扫码关注云+社区

领取腾讯云代金券