java内存模型(代码片段)

皓洲 皓洲     2022-12-13     406

关键词:

Java 内存模型

Java 内存模型

参考视频:https://www.bilibili.com/video/BV1F64y1B7sV

参考博客:https://zhuanlan.zhihu.com/p/29881777

硬件内存模型

周所周知:CPU的处理速度和内存的读写读写速度是不在一个数量级的,所以需要CPU在内存之间加上缓存来进行提速,这就呈现了一种CPU-寄存器-缓存-主存的访问结构。

多CUP缓存产生的同步问题

当一台计算机出现了多个CPU的时候就会出现如下的问题:

  • 假如CPU A将数据D从主存读取到独占的缓存内,将D改为D1
  • 同时CPU B将数据D从主存读取到独占的缓存内,将D改为D2
  • 然后A B同时将数据D写入主存中,那么主存的值会变成D1还是D2?

针对这个多个CPU缓存之间的同步问题,科学家们制定了缓存一致性协议

CPU指令重排

关于缓存一致性协议,可以猜想的是,其内容一定是一些和数据同步相关的操作。

既然要进行数据同步,就很可能出现等待、唤醒这样的操作,这可能导致性能问题,尤其是对于CPU这种运算速度极快的组件来说,丝毫的等待都是极大的浪费。

比如CPU B想要读取数据D的时候,还要等待CPU A将D写回主存。这种行为是难以忍受的。

因此,计算机科学家们做了一些优化,怎么优化呢?整体思想就是将同步改成异步。

比如CPU B要读取数据D时,发现D正在被其他CPU进行修改,那么此时CPU B注册一个读取D的消息,自己回头去做其他的事情,其他CPU写回数据D后 响应了这个注册消息。此时CPU B发现了这个消息被相应后,再去读取D 这样就能够提升效率。

但是对于CPU B来说,程序看上去就不是顺序执行的了,有可能会出现先运行后面的指令,再回头去运行前面的指令,这一种行为就体出了一种指令重排序

Java内存模型

Java内存模型屏蔽了各种硬件和操作系统的内存访问差异,实现了让Java程序能够在各种硬件平台下都能够按照预期的方式来运行。

它的抽象如图所示:

这里说明一下:

Java内存模型中的线程的工作内存(working memory)是cpu的寄存器和高速缓存的抽象描述。而JVM的静态内存储模型(JVM内存模型)只是一种对内存的物理划分而已,它只局限在内存,而且只局限在JVM的内存。

我们可以将工作线程和本地内存具象化为Thread Stack,将主存具象为Heap

Thread Stack有两种类型的变量:

  1. 原始类型的变量,比如(int、char等)总是存储在线程栈上。
  2. 对象类型变量,引用(指针)本身存储在线程栈上,而引用指向的对象存储在堆上。

在Heap中存储对象本身,持有对象引用的线程就能够访问该对象,Heap本身不关心哪个线程正在访问对象。

我们可以这么理解:Java线程模型中Thread Stack和Heap都是对物理内存的抽象。

Thread Stack大部分都是使用寄存器和CPU缓存来实现的,而Heap中需要储存大量的对象,需要大量的容量,所以大部分是使用主存来实现的。

线程通信中可能存在的问题

  • 可见性问题

    假如本地内存A和本地内存B中存在着数据x的副本,且x值为1,当线程A将X修改为2并且将数据写入主存后,当线程B想要读取X,默认的会从本地内存B中读取,而本地内存B的x依然是等于1的。换言之,线程A刷新了主存中x的值,线程B却不知道x的值发生了改变。

  • 原子性问题

    假如线程A和线程B同时读取主存中x的值,此时x为1。当线程A和线程B对x进行自增1后,放回主存,此时主存中x值变成2,但是x经过线程A和线程B的两次自增,x的值应该变为3。

并发三要素

这些问题呢,被总结为三个要素:可见性、原子性、有序性

可见性

可见性指的是,当一个线程修改共享变量的值,其他线程需要能够立即得知这个修改

  • 解读一

    线程A修改了数据D,线程B需要督导修改后最新的D。(由刷新主存的时机引起的)

    写一个例子:线程T1,在a!=2的时候进行死循环,一秒钟后,线程T2将a的值改为2。此时如果线程T1能够读到a的值被改为2,那么会跳出死循环。可是事实上线程T1会一直进行死循环。可以证明他们不满足可见性

static int a = 1;
public static void main(String[] args) throws InterruptedException 
    Thread t1 = new Thread(new Runnable() 
        @Override
        public void run() 
            while (a!=2);//只要a!=2就死循环
        
    );

    Thread t2 = new Thread(new Runnable() 
        @Override
        public void run() 
            a=2;
        
    );

    t1.start();
    Thread.sleep(1000);
    t2.start();

解决方法1:使用volatile关键字。写volatile变量,主动写主存;读volatile变量,主动读主存。

解决方法1:使用synchronized关键字。synchronized块内部读写变量,隐式调用内存lock,unlock指令,主动读主存。

  • 解读二

    线程B需要读到被修改的变量D,线程A应该修改但是因为重排序导致线程A没有及时修改变量D。(由指令重排序引起的)

举个例子:下面代码执行到代码4时,变量b是否等于1?

static int a = 0;
static boolean flag = false;
public static void main(String[] args) throws InterruptedException 
    Thread t1 = new Thread(new Runnable() 
        @Override
        public void run() 
            a=1;    // 代码1
            flag=true;  //代码2
        
    );

    Thread t2 = new Thread(new Runnable() 
        @Override
        public void run() 
            if(flag)   // 代码3
                int b = a;  // 代码4
            
        
    );

    t1.start();
    t2.start();

答案是否定的,我们在最上面提到过硬件内存模型中存在者指令重排序机制,Java内存模型中也存在指令重排序,它们的作用和约束都是一样的,第一是为了更高的执行效率,第二是在单线程中重拍后能够保证程序执行结果的正确性,就是说和顺序执行的结果是一样的。

解决方法1:使用volatile关键字。禁止volatile变量与之前的语句进行重拍,volatile这行相当于“基准线”当运行到基准线,能保证之前语句和自身的可见性。将flag加上volatile关键字就可以解决问题了。

解决方法1:使用synchronized关键字。synchronized块内部读写变量,隐式调用内存lock,unlock指令,主动读主存。使用synchronized将代码1和代码2进行包裹,这样其内部无论如何进行重排,外部只能看到最终结果,这样也保证了可见性。

原子性

原子性指的是,一个操作时不可中断的,要么全部执行,要么全部失败。

  1. 单指令原子操作
  2. 利用锁机制的原子操作,反应到上层就是synchronized关键字

有序性

上文其实我们已经详细讲解到了无论是从硬件内存模型还是Java内存模型来看,都支持指令重排这种优化操作,在单线程中虽然指令可能会被重排序,但是在单线程中内存模型能够保证执行结果的准确性,也就是说在单线程中无论指令如何重排,他最终的执行结果和顺序执行的结果时一样的,但是在多线程环境下就可能因为重排而导致一些问题。

我们在上面就提到过了由于指令重排引起的可见性问题,所以在这里不在继续讲了。

java内存模型(代码片段)

Java内存模型Java内存模型硬件内存模型多CUP缓存产生的同步问题CPU指令重排Java内存模型线程通信中可能存在的问题并发三要素可见性原子性有序性Java内存模型参考视频:https://www.bilibili.com/video/BV1F64y1B7sV参考博客:https://z... 查看详情

重新认识java内存模型(jmm)(代码片段)

通过学习《深入理解Java虚拟机》有关Java内存模型的介绍,整理的学习笔记,供你参考。文章目录Java内存模型定义主内存和工作内存CPU普及内存间交互操作对于volatile型变量的特殊规则原子性、可见性与有序性先行发生&#x... 查看详情

java并发编程:java内存模型(代码片段)

一、Java内存模型基础1.两个关键问题2.Java内存模型的抽象结构3.指令序列的重排序4.并发编程模型的分类5.happens-before二、指令重排序1.数据依赖性2.as-if-serial语义3.重排序对多线程的影响三、顺序一致性内存模型1、数据竞争与顺序... 查看详情

浅谈jvm内存结构,java内存模型和java对象模型(代码片段)

Java虚拟机正文开始@Assassin目录Java虚拟机1.JVM内存结构:1.1Java虚拟机栈:1.2堆:1.3方法区:2.Java内存模型:3.Java对象模型:4.三者区别:1.JVM内存结构:Java代码是要运行在Java虚拟机上的,而虚拟机在执行Java程序的过程中会... 查看详情

全面理解java内存模型(代码片段)

Java内存模型即JavaMemoryModel,简称JMM。JMM定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java内存... 查看详情

从cpu说起,深入理解java内存模型!(代码片段)

Java内存模型,许多人会错误地理解成JVM的内存模型。但实际上,这两者是完全不同的东西。Java内存模型定义了Java语言如何与内存进行交互,具体地说是Java语言运行时的变量,如何与我们的硬件内存进行交互的。... 查看详情

java内存模型和线程安全(代码片段)

Java内存模型和线程安全Java内存模型引言volatile关键字synchronized关键字Java线程Java线程安全synchronized锁优化锁优化技巧列举自旋锁锁消除锁粗化具体实现轻量级锁偏向锁Java内存模型引言对于多核处理器而言,每个核都会有自己单独... 查看详情

jvm内存结构java内存模型和java对象模型(代码片段)

Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文要讨论的JVM内存结构、Java内存模型和Java对象模型,这就是三个截然不同... 查看详情

java-内存模型(jsr-133)(代码片段)

Java内存模型(JavaMemoryModel,JMM)看上去和Java内存结构(JVM运行时内存结构)差不多,但这两者并不是一回事。JMM并不像JVM内存结构一样是真实存在的,它只是一个抽象的概念。Java的线程间通过共享内存(Java堆和方法区)进行... 查看详情

java内存模型并发三个特性(代码片段)

目录一.内存模型的相关概念2.并发编程中的三个概念1.原子性2.可见性3.有序性3.java内存模型1.原子性2.可见性3.有序性https://www.cnblogs.com/dolphin0520/p/3920373.html此前需要了解简单Thread实现一.内存模型的相关概念计算机在执行程序的时... 查看详情

java内存模型(代码片段)

Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括volatile、synchronized和final三个关键字,以及六项Happens-Before规则。Happens-Before的7个规则:(1).程序次序规则:在一个线程内,按照程序代码顺... 查看详情

十分钟吃透java内存模型(代码片段)

十分钟吃透Java内存模型Java内存模型把Java虚拟机内部划分为线程栈和堆。这张图演示了Java内存模型的逻辑视图。每个运行在JVM里的线程都拥有自己的线程栈。线程栈包含了该线程调用的方法的当前执行点相关的信息。一个线程... 查看详情

浅谈java的内存模型以及交互(代码片段)

本文的内存模型只写虚拟机内存模型,物理机的不予描述。 Java内存模型  在Java中,虚拟机将运行时区域分成6中,如下图:            程序计数器:用来记录当前线程执行到哪一步操作。... 查看详情

面试官:说说什么是java内存模型(jmm)?(代码片段)

本文禁止转载1.为什么要有内存模型?1.1.硬件内存架构1.2.缓存一致性问题1.3.处理器优化和指令重排序2.并发编程的问题3.Java内存模型3.1.Java运行时内存区域与硬件内存的关系3.2.Java线程与主内存的关系3.3.线程间通信4.有态度... 查看详情

javaconcurrenthashmap(代码片段)

Java内存模型由于ConcurrentHashMap是建立在Java内存模型基础上的,为了更好的理解ConcurrentHashMap,让我们首先来了解一下Java的内存模型。Java语言的内存模型由一些规则组成,这些规则确定线程对内存的访问如何排序以及... 查看详情

java内存模型(代码片段)

...序的实体是线程,每个线程JVM都会为其创建一个工作内存,工作内存是每个线程的的私有数据区域,java内存模型规定中的变量都存储在主内存& 查看详情

从硬件缓存模型到java内存模型原理浅析(代码片段)

...r?一、硬件方面的问题1、背景在现代系统的CPU中,所有的内存访问都是通过层层缓存进行的。CPU的读/写(以及指令)单元正常情况下甚至都不能直接与内存进行访问,这是物理结构决定的。CPU和缓存进行通信,而缓存才能与内... 查看详情

java内存模型jmm详解!(代码片段)

...一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见性、是否可以重排序等问题的无关具体平台的统一的保证。(可能在术语上与Java运行时内存分布有歧义,后者指堆、方法区、线程栈等内存区域)。并发编程有多种... 查看详情