首页 > 其他分享 >面试官:小伙子,能聊明白JMM给你SSP!我:嘚吧嘚吧一万字,直接征服面试官!

面试官:小伙子,能聊明白JMM给你SSP!我:嘚吧嘚吧一万字,直接征服面试官!

时间:2024-03-16 18:22:23浏览次数:30  
标签:面试官 Java SSP v1 v2 线程 内存 JMM CPU

写在开头

面试官:小伙子,JMM了解吗?
我:JMM(Java Memory Model),Java内存模型呀,学过的!
面试官:那能给我详细的聊一聊吗,越详细越好!
我:嗯~,确定越详细越好?起码得说一万字,您有时间听完?
面试官:你要是真能说一万字全是干货的话,我当场拍板要你,给你SSP!
我:这可是您说的,瞧好吧!

为了拿到一个SSP级别的Offer,我开始疯狂运转我的大脑,将过去背的八股文与自我理解总结相结合,展开了对JMM(Java内存模型)漫长的介绍,内容有点长,同志们保持耐心看完哈。

JMM诞生的背景

在这篇文章《关于Java并发多线程的一点思考》中我们提到过Java多线程存在的问题,其中有一个关于多线程的原子性、可见性、有序性问题,当时针对这个问题我们给出过如下解释:

“在一个Java程序或者说进程运行的过程中,会涉及到CPU、内存、IO设备,这三者在读写速度上存在着巨大差异:CPU速度-优于-内存的速度-优于-IO设备的速度

为了平衡这三者之间的速度差异,达到程序响应最大化,计算机、操作系统、编译器都做出了自己的努力。

  • 计算机体系结构:给 CPU 增加了缓存,均衡 CPU 和内存的速度差异;
  • 操作系统:增加了进程与线程,分时复用 CPU,均衡 CPU 和 IO 设备的速度差异;
  • 编译器:增加了指令执行重排序(这个也会带来另外的问题,我们在后面的学习中会提到),更好地利用缓存,提高程序的执行速度。

这种优化是充分必要的,但这种优化同时会给多线程程序带来原子性、可见性和有序性的问题。”

为了解决以上问题Java内存模型(JMM)应运而生,当然,早期的JMM存在着很多问题,比如非常容易消弱编译器的优化能力,但从JDK5开始,提出了JSR-133(Java Memory Model and Thread Specification Revision),用以规范和修订Java内存模型与线程,我们接下来所提及的JMM都是基于新规范的。

JMM如何解决问题

对于 Java 来说,你可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,除了抽象了线程和主内存之间的关系之外,其还规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。

开发者们可以利用这些规范,方便安全的使用多线程,甚至于不需要在乎它底层的原理,直接使用一些关键字和类(例如:volatile、synchronized、final,各种 Lock)就可以使多线程变得安全。

深刻理解JMM

为了更深刻的理解JMM,我们需要理解几个概念:Java内存区域CPU缓存指令重排序

Java内存模型与Java内存区域的区别?

这个问题是很多Java初学者容易搞混淆的,也是很多面试官在面试时喜欢考察的小知识点,虽然名字很相似,但它们的区别却很大。

Java内存模型: 主要针对的是多线程环境下,如何在主内存与工作内存之间安全地执行操作,包括变量的可见性、指令重排、原子操作等,旨在解决由于多线程并发编程带来的一些问题,它是一种规范或者说约束。

原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性;
可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到;
有序性:程序执行的顺序按照代码的先后顺序执行;

Java内存区域: 是指Java程序在JVM上运行时所流转的区域,因此也叫"Java运行时内存区域",主要包括以下几个部分(这里指JDK1.7,在1.8后方法区被元空间替代,在后面的JVM学习中会详细讲述):

  1. 方法区:存储了每一个类的结构信息,如运行时常量池、字段和方法数据、构造方法和普通方法的字节码内容。
  2. :几乎所有的对象实例以及数组都在这里分配内存。这是 Java 内存管理的主要区域。
  3. :每一个线程有一个私有的栈,每一次方法调用都会创建一个新的栈帧,用于存储局部变量、操作数栈、动态链接、方法出口等信息。所有的栈帧都是在方法调用和方法执行完成之后创建和销毁的。
  4. 本地方法栈:与栈类似,不过本地方法栈为 JVM 使用到的 native 方法服务。
  5. 程序计数器:每个线程都有一个独立的程序计数器,用于指示当前线程执行到了字节码的哪一行。
    image

CPU缓存

在上文中我们提到过CPU、内存、IO设备,这三者在读写速度存在差异,而CPU 缓存就是为了解决 CPU 处理速度和内存处理速度不对等的问题。
image

如上图为一个4核CPU的缓存架构图,在CPU缓存中一般分为3级,越靠近CPU的缓存,速度越快,价格越高,L1与L2为CPU私有,L3为多CPU共用缓存。

CPU缓存的工作方式:先将数据复制到CPU缓存中,查询时一级级向下查找,一旦找到结果就返回,不再向下遍历,若三级缓存都没查到,才会去主存(内存)中去查,然后开始运算,并将运算结果写回主存中,这时若发生多线程同时读写的话,就会存在可见性(内存缓存不一致)问题,我们写个小demo模拟一下。

【代码示例1】

public class Test {
    //是否停止 变量
    private static boolean stop = false;
    public static void main(String[] args) throws InterruptedException {
        //启动线程 1,当 stop 为 true,结束循环
        new Thread(() -> {
            System.out.println("线程 1 正在运行...");
            while (!stop) ;
            System.out.println("线程 1 终止");
        }).start();
        //休眠 1 秒
        Thread.sleep(1000);
        //启动线程 2, 设置 stop = true
        new Thread(() -> {
            System.out.println("线程 2 正在运行...");
            stop = true;
            System.out.println("设置 stop 变量为 true.");
        }).start();
    }
}

输出:

线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.

原因:
我们会发现,线程1运行起来后,休眠1秒,启动线程2,可即便线程2把stop设置为true了,线程1仍然没有停止,这个就是因为 CPU 缓存导致的可见性导致的问题。线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。
image

解决办法:

JMM告诉我们可以通过 volatile、synchronized、Lock接口、Atomic 类型保障可见性;还有一种就是在缓存与主存之间增加缓存一致性协议,如MSI,MESI等协议,协议包括CPU 高速缓存与主内存交互的时候需要遵守的原则和规范!

这个协议今天就不展开了,后面找时间再单独更新一篇,毕竟在把它整出来,面试官没耐心听下去了。

标签:面试官,Java,SSP,v1,v2,线程,内存,JMM,CPU
From: https://www.cnblogs.com/JavaBuild/p/18077394

相关文章

  • 面试官:说说反射的底层实现原理?
    反射是Java面试中必问的面试题,但只有很少人能真正的理解“反射”并讲明白反射,更别说能说清楚它的底层实现原理了。所以本文就通过大白话的方式来系统的讲解一下反射,希望大家看完之后能真正的理解并掌握“反射”这项技术。1.什么是反射?反射在程序运行期间动态获取类和操纵类的......
  • 通过DCERPC和ntlmssp获取Windows远程主机信息
    最初首发于:https://www.freebuf.com/articles/system/334948.html前言本文通过利用DCERPC协议的ping,并附加NTLMSSP认证信息来获取获取windows远程主机的版本号,主机名,所在域的域名,DNS等信息。因为通过rpc进行探测的工具,大部分都是依托impacket来实现,而实战中通过挂代理进行内网......
  • 一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!
    写在开头在线程的生命周期中,不同状态之间切换时,可以通过调用sleep()、wait()、join()、yield()等方法进行线程状态控制,针对这一部分知识点,面试官们也会做做文章,比如问你这些方法的作用以及之间的区别。那么今天我们就一起来总结一下这几个方法的作用及区别,先画一个思维导图梳理一......
  • 面试官:Spring Boot 微服务中你使用了哪些 starter maven 依赖项?这些 starter 到底是什
    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点面试官:SpringBoot微服务中你使用了哪些startermaven依赖项在SpringBoot微服务中,可以使用大量的StarterMaven依赖项来简化项目配置和快速集成各种功能,包括......
  • 面试官:说说反射的底层实现原理?
    反射是Java面试中必问的面试题,但只有很少人能真正的理解“反射”并讲明白反射,更别说能说清楚它的底层实现原理了。所以本文就通过大白话的方式来系统的讲解一下反射,希望大家看完之后能真正的理解并掌握“反射”这项技术。1.什么是反射?反射在程序运行期间动态获取类和操纵类的......
  • 面试官:你还有什么想问我的?
    最近找我辅导面试的同学很多,我总结了一些案例,发现很多同学在面试环节,特别是面试官提问完毕的反问环节,经常犯一些比较低级的错误。当然,也不能完全称之为错误,毕竟网上很多所谓的面试教程都是这么教的。但在实际的面试过程中,如果你真的这么问,就可能会影响你的最终面试结果。挑选了......
  • 面试官:Spring Boot 实现全局异常处理应该怎么实现
    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点面试官:SpringBoot实现全局异常处理应该怎么实现在SpringBoot中实现全局异常处理可以帮助我们统一处理应用程序中的异常,提高代码的可维护性和可读性。通常情况......
  • UVM宏解释+odt文件转doc+merge命令和difflib+python调用命令+clog2和系统函数+java添
    UVM宏解释UVM_DISABLE_AUTO_ITEM_RECORDINGhttps://blog.csdn.net/MGoop/article/details/127295965itemrecord的方法主要是用于记录事务信息的,原理是调用accept_tr,begin_tr,end_tr。似乎和波形上显示出各个事务相关。默认情况下,在调用get_next_item()和item_done()时自动......
  • 面试官:说说线程池的工作原理?
    线程池的底层是基于线程和任务队列来实现的,创建线程池的创建方式通常有以下两种:普通Java项目,使用ThreadPoolExecutor来创建线程池,这点《阿里巴巴Java开发手册》中也有说明,如下图所示:Spring项目中,会使用代码可读性更高的ThreadPoolTaskExecutor来创建线程池,虽然它的......
  • 面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会
    写在开头面试官:小伙子请聊一聊Java中的精灵线程?我:什么?精灵线程?啥时候精灵线程?面试官:精灵线程没听过?那守护线程呢?我:守护线程知道,就是为普通线程服务的线程嘛。面试官:没了?守护线程的特点,怎么使用,需要注意啥,Java中经典的守护线程都有啥?我:不知道。。。这的天,面试一个10K的工作,......