Java并发编程面试题——线程安全(原子性、可见性、有序性)

news2024/7/6 20:06:18

文章目录

  • 一、原子性高频问题
    • 1.1 Java中如何实现线程安全?
    • 1.2 CAS底层实现
    • 1.3 CAS的常见问题
    • 1.4 四种引用类型 + ThreadLocal的问题?
  • 二、可见性高频问题
    • 2.1 Java的内存模型
    • 2.2 保证可见性的方式
    • 2.3 volatile修饰引用数据类型
    • 2.4 有了MESI协议,为啥还有volatile?
    • 2.5 volatile的可见性底层实现
  • 三、有序性高频问题
    • 3.1 什么是有序性问题
    • 3.2 volatile的有序性底层实现
  • 四、synchronized高频问题
    • 4.1 synchronized锁升级的过程?
    • 4.2 synchronized锁粗化&锁消除?
    • 4.3 synchronized实现互斥性的原理?
    • 4.4 wait为什么是Object下的方法?

一、原子性高频问题

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

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

实现线程安全方式:

  • 悲观锁:synchronized,lock(AQS)
  • 乐观锁:CAS

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

1.2 CAS底层实现

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

CAS在Java层面最多就能看到native方法,可以去看Unsafe类中提供的CAS操作

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

image.png

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

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

image.png

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

cmpxchg ,是汇编的指令,CPU硬件底层就支持 比较和交换 (cmpxchg),cmpxchg并不保证原子性的,如果是多核的操作系统,需要追加一个lock指令。

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

1.3 CAS的常见问题

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

    比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。

    解决方案很简单,Java端已经提供了,JUC下提供的AtomicStampedReference就可以实现。修改value的同时,指定好版本号,一旦版本号和数据的版本号不一致就执行失败。
    image.png

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

    解决方案:

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

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

ThreadLocal的问题:见Java基础面试题——IO和多线程专题

四种引用类型:

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

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

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

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

二、可见性高频问题

2.1 Java的内存模型

在处理指令时,CPU会拉取数据,优先级是从L1(线程独享)到L2(内核独享)到L3(多核共享),如果都没有,需要去主内存中拉取,JMM就是在CPU和主内存之间,来协调,保证可见、有序性等操作。

Java Memory Model
image.png

2.2 保证可见性的方式

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

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

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

2.3 volatile修饰引用数据类型

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

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

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

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

2.5 volatile的可见性底层实现

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

三、有序性高频问题

3.1 什么是有序性问题

在Java编译.java为.class时,会基于JIT做优化,将指令的顺序做调整,从而提升执行效率。在CPU层面,也会对一些执行进行重新排序,从而提升执行效率。这种指令的调整,在一些特殊的操作上,会导致出现问题。

单例模式中的懒汉机制中,就存在一个这样的问题。懒汉为了保证线程安全,一般会采用DCL的方式。

3.2 volatile的有序性底层实现

被volatile修饰的属性,在编译时,会在前后追加 内存屏障 ,这个内存屏障是JDK规定的,目的是保证volatile修饰的属性不会出现指令重排的问题。

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

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

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

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

image.png

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下的方法?

  1. wait方法是在持有sync锁的时候释放锁资源。
  2. sync锁的是对象也就是Object。
  3. 所以wait自然是Object下的方法。

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

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

相关文章

2022年全国职业院校技能大赛网络安全竞赛A模块(2)

2022年全国职业院校技能大赛(中职组) 网络安全竞赛试题 (2) 目录 2022年全国职业院校技能大赛(中职组) 网络安全竞赛试题 (2) 模块A 基础设施设置与安全加固 A-1任务一 登录安…

ChatGPT引爆资本狂潮,AI大战升级版打响!谷歌紧急上线对标竞品,微软测试AI搜索引擎,百度宣国产版定档,谁才是最后的赢家?

文 BFT机器人 如果你迄今为止还没有听说过ChatGPT,那就已经彻彻底底被人工智能的新时代甩到身后了。 如果说去年年底ChatGPT刚推出的时候,大多数网友对其的态度还是认为它和Clubhouse一样仅会是昙花一现的话,那么2个月时间过去,C…

【Ubuntu换源教程】不同Ubuntu系统版本换清华源

今天在新电脑上装了虚拟机VMware Workstation Pro 16,在虚拟机上安装了Ubuntu20.04系统。 在做Ubuntu20.04系统换源的时候,发现源要和Ubuntu的版本匹配,之前一直不知道,一直都是盲目换源,版本如果不匹配的话&#xff…

【日常】矩阵正态分布参数检验问题

最近给凯爹做的一个苦力活,统计检验这个东西说实话也挺有趣,跟算法设计一样,好的检验真的是挺难设计的,就有近似算法的那种感觉,检验很难保证size和power都很理想,所以就要做tradeoff,感觉这个假…

DPR-34 AC22V【双位置继电器】

系列型号: DPR-20双位置继电器;DPR-31双位置继电器; DPR-32双位置继电器;DPR-33双位置继电器; DPR-34双位置继电器;DPR-35双位置继电器; DPR-11双位置继电器;DPR-12双位置继电器&…

Python-项目实战--贪吃蛇小游戏-游戏框架搭建(3)

1.游戏框架搭建介绍pygame开发图像界面游戏的几个要素,并且把贪吃蛇游戏的整体框架搭建完成本节知识点包括:pygame的初始化和退出游戏主窗口游戏循环和游戏时钟主窗口背景颜色绘制文本pygame的坐标系游戏事件监听绘制图形定时器事件1.5绘制文本pygame的f…

Nuclei文*件上*传FUZZ POC

目录 1.前言 2. Nuclei文件上传FUZZ POC 3. 实战中的应用 1.前言 该文件上传FUZZ POC主要来源于一个靶*场,该POC 主要用来FUZZ目标js页面中的upload ajax请求,以此来进一步尝试文件上传漏*洞利*用。 这里也要感谢下“打工仔1号”提供的开*发人员常见的文*件上*传javaScr…

设计模式(九)----结构型模式之代理模式

一、结构型模式 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制或者实现机制来组织接口和类,后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低,满…

数字孪生|可视化图表之堆叠柱状图

一、含义 堆叠柱状图(Stacked Bar Chart)将每个柱子进行分割以显示相同类型下各个数据的大小情况,它将多个数据集的柱子彼此叠加显示,适合用来显示大类别如何细分为较小的类别,以及每部分与总量之间的关系。在展示不同…

Lecture5 实现线性回归(Linear Regression with PyTorch)

目录 1 Pytorch实现线性回归 1.1 实现思路 1.2 完整代码 2 各部分代码逐行详解 2.1 准备数据集 2.2 设计模型 2.2.1 代码 2.2.2 代码逐行详解 2.2.3 疑难点解答 2.3 构建损失函数和优化器 2.4 训练周期 2.5 测试结果 3 线性回归中常用优化器 1 Pytorch实现线性回归…

网络协议(七):传输层-UDP

网络协议系列文章 网络协议(一):基本概念、计算机之间的连接方式 网络协议(二):MAC地址、IP地址、子网掩码、子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类、ISP、上网方式、公网私网、NAT 网络…

06- 信用卡反欺诈 (机器学习集成算法) (项目六)

本项目为 kaggle 项目 项目难点在于: 盗刷的比例占总数据量的比例较低, 直接预测为非盗刷也有 99.8% 的准确率.data.info() # 查看所有信息msno.matrix(data) # 查看缺失值axis1 时 # 删除列显示颜色种类 from matplotlib import colors plt.colormaps() # mag…

关于知识图谱TransR

论文题目 Learning Entity and Relation Embeddings for Knowledge Graph Completion 论文链接 TransR 文中指出,不管是TransE还是TransH都是将实体和关系映射同一空间,但是,一个实体可能具有多个层面的信息,不同的关系可能关注…

ray简单介绍

ray使用也有一段时间了, 这篇文章总结下ray的使用场景和用法 ray可以做什么? 总结就两点: 可以将其视为一个进程池(当然不仅限于此), 可以用于开发并发应用还可以将应用改造为分布式 基于以上两点, 有人称之为:Modern Parallel and Distributed Python 构成 Ray AI Runtim…

Redis多级缓存

文章目录一. 什么是多级缓存二. JVM进程缓存一. 什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: 请求要经过Tomcat处理,Tomcat的性能…

Linux高级IO

文章目录一、五种 IO 模型1.阻塞 IO2.非阻塞 IO3.信号驱动 IO4. IO 多路转接5.异步 IO二、高级 IO 重要概念1.同步通信和异步通信2.阻塞和非阻塞fcntl 系统调用3.其他高级 IO三、I/O 多路转接之 select1.函数原型socket 就绪的条件2.理解 select 的执行过程3.使用示例4. select…

新手小白如何入门黑客技术?

你是否对黑客技术感兴趣呢?感觉成为黑客是一件很酷的事。那么作为新手小白,我们该如何入门黑客技术,黑客技术又是学什么呢? 其实不管你想在哪个新的领域里有所收获,你需要考虑以下几个问题: 首先&#xff…

Springboot扩展点之FactoryBean

前言FactoryBean是一个有意思,且非常重要的扩展点,之所以说是有意思,是因为它老是被拿来与另一个名字比较类似的BeanFactory来比较,特别是在面试当中,动不动就问你:你了解Beanfactory和FactoryBean的区别吗…

spring cloud gateway网关和链路监控

文章目录 目录 文章目录 前言 一、网关 1.1 gateway介绍 1.2 如何使用gateway 1.3 网关优化 1.4自定义断言和过滤器 1.4.1 自定义断言 二、Sleuth--链路追踪 2.1 链路追踪介绍 2.2 Sleuth介绍 2.3 使用 2.4 Zipkin的集成 2.5 使用可视化zipkin来监控微服务 总结 前言 一、网关…

ubuntu wordpress建站

nginx 安装测试 https://blog.csdn.net/leon_zeng0/article/details/113578143 ubuntu 基于apache2安装wordpress https://ubuntu.com/tutorials/install-and-configure-wordpress#7-configure-wordpress 报错403的话,是权限没搞对,解决参考https://ww…