并发编程面试题1

news2024/10/7 12:27:02

并发编程面试题1

一、原子性高频问题:

1.1 Java中如何实现线程安全?

多线程操作共享数据出现的问题。

锁:

  • 悲观锁:synchronized,lock
  • 乐观锁:CAS

可以根据业务情况,选择ThreadLocal,让每个线程玩自己的数据。

1.2 CAS底层实现

最终回答:先从比较和交换的角度去聊清楚,在Java端聊到native方法,然后再聊到C++中的cmpxchg的指令,再聊到lock指令保证cmpxchg原子性

Java的角度,CAS在Java层面最多你就能看到native方法。

你会知道比较和交换:

  • 先比较一下值是否与预期值一致,如果一致,交换,返回true
  • 先比较一下值是否与预期值一致,如果不一致,不交换,返回false

可以去看Unsafe类中提供的CAS操作

四个参数:哪个对象,哪个属性的内存偏移量,oldValue,newValue

image.png

native是直接调用本地依赖库C++中的方法。

https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/unsafe.cpp

image.png

https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp

在CAS底层,如果是多核的操作系统,需要追加一个lock指令

单核不需要加,因为cmpxchg是一行指令,不能再被拆分了

image.png

看到cmpxchg ,是汇编的指令,CPU硬件底层就支持 比较和交换 (cmpxchg),cmpxchg并不保证原子性的。(cmpxchg的操作是不能再拆分的指令)

所以才会出现判断CPU是否是多核,如果是多核就追加lock指令。

lock指令你可以理解为是CPU层面的锁,一般锁的粒度就是 缓存行 级别的锁,当然也有 总线锁 ,但是成本太高,CPU会根据情况选择。

1.3 CAS的常见问题

ABA: ABA不一定是问题!因为一些只存在 ++,–的这种操作,即便出现ABA问题,也不影响结果!

线程A:期望将value从A1 - B2

线程B:期望将value从B2 - A3

线程C:期望将value从A1 - C4

按照原子性来说,无法保证线程安全。

解决方案很简单,Java端已经提供了。

image.png

说人话就是,在修改value的同时,指定好版本号。

JUC下提供的AtomicStampedReference就可以实现。

自旋次数过多:

自旋次数过多,会额外的占用大量的CPU资源!浪费资源。

回答方式:可以从synchronized或者LongAdder层面去聊

  • synchronized方向:从CAS几次失败后,就将线程挂起(WAITING),避免占用CPU过多的资源!
  • LongAdder方向:这里是基于类似 分段锁 的形式去解决(要看业务,有限制的),传统的AtmoicLong是针对内存中唯一的一个值去++,LongAdder在内存中搞了好多个值,多个线程去加不同的值,当你需要结果时,我将所有值累加,返回给你。

只针对一个属性保证原子性: 处理方案,学了AQS就懂了。ReentrantLock基于AQS实现,AQS基于CAS实现核心功能。

1.4 四种引用类型 + ThreadLocal的问题?

ThreadLocal的问题:Java基础面试题2 – 第16题。

四种引用类型:

  • 强引用:User xx = new User(); xx就是强引用,只要引用还在,GC就不会回收!

  • 软引用:用一个SofeReference引用的对象,就是软引用,如果内存空间不足,才会回收只有软引用指向对象。 一般用于做缓存

    SoftwareReference xx = new SoftwareReference (new User);
    
    User user = xx.get();
    
  • 弱引用:WeakReference引用的对象,一般就是弱引用,只要执行GC,就会回收只有弱引用指向的对象。可以解决内存泄漏的问题 ,看ThreadLocal即可

    ThreadLocal的问题:Java基础面试题2 – 第16题。

  • 虚引用:PhantomReference引用的对象,就是虚引用,拿不到虚引用指向的对象,一般监听GC回收阶段,或者是回收堆外内存时使用。

二、可见性高频问题:

2.1 Java的内存模型

回答方式。先全局描述。 在处理指令时,CPU会拉取数据,优先级是从L1到L2到L3,如果都没有,需要去主内存中拉取,JMM就是在CPU和主内存之间,来协调,保证可见、有序性等操作。

一定要聊JMM,别上来就聊JVM的内存结构,不是一个东西!!!!(Java Memory Model)

image.png

CPU核心,就是CPU核心(寄存器)

缓存是CPU的缓存,CPU的缓存分为L1(线程独享),L2(内核独享),L3(多核共享)

JMM就是Java内存模型的核心,可见性,有序性都基于这实现。

主内存JVM,就是你堆内存。

2.2 保证可见性的方式

啥是可见性: 可见性是指线程间的,对变量的变化是否可见

Java层面中,保证可见性的方式有很多:

  • volatile,用volatile基本数据类型,可以保证每次CPU去操作数据时,都直接去主内存进行读写。
  • synchronized,synchronized的内存语义可以保证在获取锁之后,可以保证前面操作的数据是可见的。
  • lock(CAS-volatile),也可以保证CAS或者操作volatile的变量之后,可以保证前面操作的数据是可见的。
  • final,是常量没法动~~

2.3 volatile修饰引用数据类型

先说结果, 首先volatile修饰引用数据类型,只能保证引用数据类型的地址是可见的,不保证内部属性可见。

But,这个结论只能在hotspot中实现,如果换一个版本的虚拟机,可能效果就不一样了。volatile修饰引用数据类型,JVM压根就没规范过这种操作,不同的虚拟机厂商,可以自己实现。
这个问题,只出现在面试中,干活你要这么干……………………干丫的~

2.4 有了MESI协议,为啥还有volatile?

MESI是CPU缓存一致性的协议,大多数的CPU厂商都根据MESI去实现了缓存一致性的效果。

CPU已经有MESI协议了,volatile是不是有点多余啊!?

首先,这哥俩不冲突,一个是从CPU硬件层面上的一致性,一个是Java中JMM层面的一致性。

MESI协议,他有一套固定的机制,无论你是否声明了volatile,他都会基于这个机制来保证缓存的一致性(可见性)。同时,也要清楚,如果没有MESI协议,volatile也会存在一些问题,不过也有其他的处理方案(总线锁,时间成本太高了,如果锁了总线,就一个CPU核心在干活)。

MESI是协议,是规划,是interface,他需要CPU厂商实现。

既然CPU有MESI了,为啥还要volatile,那自然是MESI协议有问题。MESI保证了多核CPU的独占cache之间的可见性,但是CPU不是说必须直接将寄存器中的数据写入到L1,因为在大多是×86架构的CPU中,寄存器和L1之间有一个store buffer,寄存器值可能落到了store buffer,没落到L1中,就会导致缓存不一致。而且除了×86架构的CPU,在arm和power的CPU中,还有load buffer,invalid queue都会或多或少影响缓存一致性!

回答的方式:MESI协议和volatile不冲突,因为MESI是CPU层面的,而CPU厂商很多实现不一样,而且CPU的架构中的一些细节也会有影响,比如Store Buffer会影响寄存器写入L1缓存,导致缓存不一致。volatile的底层生成的是汇编的lock指令,这个指令会要求强行写入主内存,并且可以忽略Store Buffer这种缓存从而达到可见性的目的,而且会利用MESI协议,让其他缓存行失效。

2.5 volatile的可见性底层实现

volatile的底层生成的是汇编的lock指令,这个指令会要求强行写入主内存,并且可以忽略Store Buffer这种缓存从而达到可见性的目的,而且会利用MESI协议,让其他缓存行失效。

三、有序性高频问题:

3.1 什么是有序性问题

单例模式中的懒汉机制中,就存在一个这样的问题。

懒汉为了保证线程安全,一般会采用DCL的方式。

但是单单用DCL,依然会有几率出现问题。

线程可能会拿到初始化一半的对象去操作,极有可能出现NullPointException。

(初始化对象三部,开辟空间,初始化内部属性,指针指向引用)

在Java编译.java为.class时,会基于JIT做优化,将指令的顺序做调整,从而提升执行效率。

在CPU层面,也会对一些执行进行重新排序,从而提升执行效率。

这种指令的调整,在一些特殊的操作上,会导致出现问题。

3.2 volatile的有序性底层实现

被volatile修饰的属性,在编译时,会在前后追加 内存屏障

SS:屏障前的读写操作,必须全部完成,再执行后续操作

SL:屏障前的写操作,必须全部完成,再执行后续读操作

LL:屏障前的读操作,必须全部完成,再执行后续读操作

LS:屏障前的读操作,必须全部完成,再执行后续写操作

image.png

这个内存屏障是JDK规定的,目的是保证volatile修饰的属性不会出现指令重排的问题。

volatile在JMM层面,保证JIT不重排可以理解,但是,CPU怎么实现的。

查看这个文档:https://gee.cs.oswego.edu/dl/jmm/cookbook.html

image.png

不同的CPU对内存屏障都有一定的支持,比如×86架构,内部自己已经实现了LS,LL,SS,只针对SL做了支持。

去openJDK再次查看,mfence是如何支持的。其实在底层还是mfence内部的lock指定,来解决指令重排问题。

image.png

四、synchronized高频问题:

4.1 synchronized锁升级的过程?

锁就是对象,随便哪一个都可以,Java中所有对象都是锁。

无锁(匿名偏向)、偏向锁、轻量级锁、重量级锁

无锁(匿名偏向): 一般情况下,new出来的一个对象,是无锁状态。因为偏向锁有延迟,在启动JVM的4s中,不存在偏向锁,但是如果关闭了偏向锁延迟的设置,new出来的对象,就是匿名偏向。

偏向锁: 当某一个线程来获取这个锁资源时,此时,就会变为偏向锁,偏向锁存储线程的ID

当偏向锁升级时,会触发偏向锁撤销,偏向锁撤销需要等到一个安全点,比如GC的时候,偏向锁撤销的成本太高,所以默认开始时,会做偏向锁延迟。

安全点:

  • GC
  • 方法返回之前
  • 调用某个方法之后
  • 甩异常的位置
  • 循环的末尾

轻量级锁: 当在出现了多个线程的竞争,就要升级为轻量级锁(有可能直接从无锁变为轻量级锁,也有可能从偏向锁升级为轻量级锁),轻量级锁的效果就是基于CAS尝试获取锁资源,这里会用到自适应自旋锁,根据上次CAS成功与否,决定这次自旋多少次。

重量级锁: 如果到了重量级锁,那就没啥说的了,如果有线程持有锁,其他竞争的,就挂起。

4.2 synchronized锁粗化&锁消除?

锁粗化(锁膨胀):(JIT优化)

while(){
   sync(){
      // 多次的获取和释放,成本太高,优化为下面这种
   }
}
//----
sync(){
   while(){
       //  优化成这样
   }
}

锁消除:在一个sync中,没有任何共享资源,也不存在锁竞争的情况,JIT编译时,就直接将锁的指令优化掉。

4.3 synchronized实现互斥性的原理?

偏向锁:查看对象头中的MarkWord里的线程ID,是否是当前线程,如果不是,就CAS尝试改,如果是,就拿到了锁资源。

轻量级锁:查看对象头中的MarkWord里的Lock Record指针指向的是否是当前线程的虚拟机栈,如果是,拿锁执行业务,如果不是CAS,尝试修改,修改他几次,不成,再升级到重量级锁。

重量级锁:查看对象头中的MarkWord里的指向的ObjectMonitor,查看owner是否是当前线程,如果不是,扔到ObjectMonitor里的EntryList中,排队,并挂起线程,等待被唤醒。

image.png

4.4 wait为什么是Object下的方法?

执行wait方法需要持有sync锁。

sync锁可以是任意对象。

同时执行wait方法是在持有sync锁的时候,释放锁资源。

其次wait方法需要去操作ObjectMonitor,而操作ObjectMonitor就必须要在持有锁资源的前提的才能操作,将当前线程扔到WaitSet等待池中。

同理,notify方法需要将WaitSet等待池中线程扔到EntryList,如果不拥有ObjectMonitor,怎么操作!

类锁就是基于类.class作为 类锁

对象锁,就是new 一个对象作为 对象锁

设计模式(单例,工厂,代理,消费者生产者,策略,责任链,观察者,模板,装饰者),多线程,JVM,MySQL,Spring,SpringBoot,Redis,MQ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/856882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023最新版Anaconda下载安装教程(非常详细)从零基础入门到精通,看完这一篇就够了

1. 前言 小编的电脑是win10系统的,这里以win10系统安装Anaconda为例,其他的系统安装过程类似,可以照猫画虎,下面请看具体的安装过程。 2. 下载软件 1、首先去官网上进行下载软件,下载地址: https://docs…

php获取随机订单号(封装函数)

作为一个开发人员,生成订单时常常需要获取一段随机码来表示订单号,并且订单号一般包含的特定的时间日期等信息,临时现写一个比较浪费时间,这里有一个封装好的生成随机订单号的函数,需要时直接调用即可。 代码如下&…

【腾讯云 Cloud Studio 实战训练营】一个新的趋势已来

写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成…

Datahub稳定版本0.10.4安装指南(独孤风版本)

大家好,我是独孤风,大数据流动的作者。 曾几何时,我在第一次安装JDK环境的时候也遇到了不小的麻烦,当时还有朋友就因为这个环境问题觉得自己根本不是编程的料,选择了放弃。当时有个段子说,“如果不是JDK环境…

生物学家呼吁:基因组测序是从大流行病中快速获得信息的最重要方法之一

生物学家Jason Ladner和Jason Sahl于2023年8月1日发表在《PLOS Biology》(IF20229.8)的一篇文章中主张,持续发展基因组测序是能从大流行病中快速获得信息的最重要方法之一。基因组测序对全球应对COVID-19产生了巨大影响,随着更多研…

与这个夏天的快乐与不快乐,都挥手告别吧!

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 伊姐 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩天津录音间 2023这个夏天, 地震、暴热、洪水……呼的一下, 密集发生的天灾让人揪心&#xff0c…

Unity-Shader-高亮Highlight

常用Shader-高亮,可动态调整高亮颜色、高亮强度范围/等级、高亮闪烁速度、高亮状态 Shader "CustomShader/Highlight" {Properties{_Color("Color", Color) (0.9044118,0.6640914,0.03325041,0)_Albedo("Albedo", 2D) "white…

vue自定义密码输入框解决浏览器自动填充密码的问题

浏览器对于type"password"的输入框会自动填充密码,但有时出于安全或者其他原因,我们不希望浏览器记住并自动填充密码。通过网上查到的一些解决方案,可以总结出以下几种解决方案(主要用edge浏览器进行测试): 通过autoco…

玩一玩通义千问Qwen开源版,Win11 RTX3060本地安装记录!

大概在两天前,阿里做了一件大事儿。 就是开源了一个低配版的通义千问模型--通义千问-7B-Chat。 这应该是国内第一个大厂开源的大语言模型吧。 虽然是低配版,但是在各类测试里面都非常能打。 官方介绍: Qwen-7B是基于Transformer的大语言模…

[JavaScript游戏开发] Q版地图上让英雄、地图都动起来

系列文章目录 第一章 2D二维地图绘制、人物移动、障碍检测 第二章 跟随人物二维动态地图绘制、自动寻径、小地图显示(人物红点显示) 第三章 绘制冰宫宝藏地图、人物鼠标点击移动、障碍检测 第四章 绘制Q版地图、键盘上下左右地图场景切换 第五章 Q版地图上让英雄、地图都动起来…

数据结构—图的遍历

6.3图的遍历 遍历定义: ​ 从已给的连通图中某一顶点出发,沿着一些边访问遍历图中所有的顶点,且使每个顶点仅被访问一次,就叫作图的遍历,它是图的基本运算。 遍历实质:找每个顶点的邻接点的过程。 图的…

数据结构笔记--链表经典高频题

前言 面经: 针对链表的题目,对于笔试可以不太在乎空间复杂度,以时间复杂度为主(能过就行,对于任何题型都一样,笔试能过就行);对于面试,时间复杂度依然处在第一位&#xf…

量化交易可视化(9)-热力图

热力图的含义 热力图是一种用颜色编码数据密度的二维图表。它的含义是通过不同颜色的渐变来显示数据的相对密度或值的大小。 热力图通常用于可视化矩阵或二维表格数据,其中每个单元格的值被映射到一个颜色,从而形成一个色阶。较小的值通常用较浅的颜色表…

许多智能算法并不智能(续)

许多智能算法被认为并不智能,主要是因为它们在某些方面仍然存在一些限制。以下是一些常见的原因: 缺乏常识和理解能力:当前的智能算法主要依赖于大量的数据和模式识别来做出决策,但它们通常缺乏对世界的常识和深层理解。这意味着它…

视觉大模型的全面解析

前言 本文主要围绕Foundational Models,即基础模型(通过自监督或半监督方式在大规模数据上训练的模型,可以适应其它多个下游任务。)这个概念,向大家全面阐述一个崭新的视觉系统。例如,通过 SAM,…

nbcio-boot因升级mybatis-plus到3.5.3.1和JSQLParser 到4.6引起的online表单开发的数据库导入出错解决

更多功能看演示系统 gitee源代码地址 后端代码: https://gitee.com/nbacheng/nbcio-boot 前端代码:https://gitee.com/nbacheng/nbcio-vue.git 在线演示(包括H5) : http://122.227.135.243:9888 nbcio-boot因升级…

【雕爷学编程】Arduino动手做(01)---干簧管传感器模块2

37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…

接龙序列(14届)

对于一个长度为 K 的整数数列:A1,A2,...,AK,我们称之为接龙数列当且仅当 Ai 的首位数字恰好等于 Ai−1的末位数字 (2≤i≤K2≤i≤K)。 例如 12,23,35,56,61,11 是接龙数列;12,23,34,56 不是接龙数列,因为 56 的首位数字不等于 34…

matplotlib 笔记:plot 折线图

1 基本用法 import numpy as np import matplotlib.pyplot as plt x np.linspace(0,10,50) y np.tan(x)**2 np.cos(x)plt.plot(x, y) plt.show() 2 linestyle 折线图类型 import numpy as np import matplotlib.pyplot as pltx np.linspace(0,10,50) y np.tan(x)**2 np…

node配置Web 服务器

1、什么是 Web 服务器 Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web服务器的基本功能就是提供Web信息浏览服务。 它只需支持HTTP协议、HTML文档格式及URL,与客户端的网络浏览器配合。 大多数 web 服务器都支持服…