多线程面试精选

Java内存模型(JMM)与线程安全

2021-06-19  本文已影响0人  _code_x

Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。用来屏蔽不同硬件和操作系统的内存访问差异,让java程序在各种平台下都能达到一致的内存访问效果。具体要点:

主内存,工作内存理解

由于不同的线程间无法访问对方线程的工作内存,线程间的通信(传值)必须通过主内存来完成。下面是传值的模型图:

JAVA线程安全之内存模型3

主内存与工作内存的数据存储类型以及操作方式

存储数据类型,根据虚拟机规范,对于一个实例对象中的成员方法而言:

操作:主内存中的实例对象可以被多线程共享,倘若两个线程同时调用了同一个对象的同一个方法,那么两条线程会将要操作的数据拷贝一份到自己的工作内存中,执行完成操作后才刷新到主内存。

内存模型和内存区域划分的区别

Java线程与硬件处理器

先来看几个概念,帮助理解Java线程与硬件处理器的关系。

Linux 线程(叫轻量级进程)模型

三个主要的概念——内核线程、轻量级进程、用户线程

JVM中线程的实现原理

流程示意图

JAVA线程安全之内存模型6

Java内存模型与硬件内存架构

硬件内存流程:

Java内存模型与硬件内存架构的关系

JAVA线程安全之内存模型7

理解线程安全问题的原因

Java内存模型的承诺

ps:如果出现数据依赖,那么就不会出现指令重排!

  • 单线程中肯定不存在这个问题,因为程序按照串行顺序进行。

  • 多线程情况下,前面我们分析过,由于线程对共享变量的操作都是线程拷贝到各自的工作内存进行操作后才写回到主内存中的,这就可能存在一个线程A修改了共享变量x的值,还未写回主内存时,另外一个线程B又对主内存中同一个共享变量x进行操作,但此时A线程工作内存中共享变量x对线程B来说并不可见,这种工作内存与主内存同步延迟现象就造成了可见性问题,另外指令重排以及编译器优化也可能导致可见性问题,通过前面的分析,我们知道无论是编译器优化还是处理器优化的重排现象,在多线程环境下,确实会导致程序轮序执行的问题,从而也就导致可见性问题,从而带来线程安全问题。

内存屏障:也叫内存栅栏,是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。ps:Store:将处理器缓存的数据刷新到内存中/刷新数据到主内存(写入)。Load:将内存存储的数据拷贝到处理器的缓存中/数据加载(读取)

通过volatile标记,可以解决编译器层面的可见性与重排序问题。而内存屏障则解决了硬件层面(处理器层面)的可见性与重排序问题!!

happen-before原则

编写的程序都要经过优化(编译器和处理器会对程序进行优化)后才会被运行,优化分为很多种,其中有一种优化叫做重排序,重排序需要遵守happens-before规则。不能说你想怎么排就怎么排,如果那样岂不是乱了套。

happens-before原则的核心是:可见性,并不是说 一个操作发生在另一个操作前面,它真正要表达的是:前面一个操作的结果对后续操作是可见的。(无论他们是否在同一个线程),一共有八个规则:包括单线程、锁、volatile、传递规则、线程的启动/中断/终止与对象的创建。

拓展:Java 内存模型也规定了 JVM 如何按需提供禁用缓存与编译优化的方法,这些方法就是 volatile、syncronized、final 三个关键字与 happen-before 原则。

巨人的肩膀

https://www.huaweicloud.com/articles/08fb82013db0fbcb43f060033d563b55.html
https://mp.weixin.qq.com/s/6Laqv1ryS_EobJNOvKcSCA

上一篇下一篇

猜你喜欢

热点阅读