2021-Java后端工程师面试指南-(并发-多线程)(上)

简介: 前言文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…种一棵树最好的时间是十年前,其次是现在

Tips


面试指南系列,很多情况下不会去深挖细节,是小六六以被面试者的角色去回顾知识的一种方式,所以我默认大部分的东西,作为面试官的你,肯定是懂的。

www.processon.com/view/link/6…

上面的是脑图地址


叨絮


可能大家觉得有点老生常谈了,确实也是。面试题,面试宝典,随便一搜,根本看不完,也看不过来,那我写这个的意义又何在呢?其实嘛我写这个的有以下的目的

  • 第一就是通过一个体系的复习,让自己前面的写的文章再重新的过一遍,总结升华嘛
  • 第二就是通过写文章帮助大家建立一个复习体系,我会将大部分会问的的知识点以点带面的形式给大家做一个导论

然后下面是前面的文章汇总

今天来看看多线程的,这块是重点,也是难点,硬核有点多哈哈。


并发


记得阿里的第一个题就是面的并发,哈哈 这个小六六得好好总结了。


聊聊Java的并发模型

这个为啥是第一个问题,肯定是有原因的,如果连Java的并发模型都不清楚,你跟我扯一堆的锁,一堆的juc有啥用呢?

  • Java并发 采用的是 共享内存模型,Java线程之前的通信总是隐式进行的。
  • Java线程通信由Java内存模型(简称 JMM)控制,JMM 决定一个线程对共享变量的写入何时对另一个线程可见。从抽象角度看,JMM定义了 线程 和 主内存 之间的抽象关系:线程之间的共享变量储存在主内存中,每个线程都有一个私有的本地内存,本地内存储存了 该线程 以读共享变量的副本。


对多线程了解吗,说说你平时怎么对临界资源的访问控制的。

其实这个题就是一个引人,由浅入深的过程,

  • 如果对应的临界资源是在单JVM的进程中,那么我们可以用Synchronized和lock
  • 对于分布式环境下的多线程中,那么就得用上分布式锁(redis 或者zookeeper实现)


那么聊聊你对Synchronized的认识吧

  • synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
  • synchronized 最主要三种用法
  • 修饰实例方法 要获得当前对象实例的锁
  • 修饰静态方法 获得当前类对象的锁
  • 修饰代码块 synchronized(this|object) 表示进入同步代码库前要获得给定对象的锁。synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁
  • synchronized 关键字最主要的二种底层实现方式:
  • synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
  • synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。


聊聊Java对象的布局

  • 首先我们知道Java对象分布由三个部分组成 对象头、实例数据、对对齐填充字节,下面我们来一个个说说
  • 对象头的组成由 Mark Word、类元数据的指针(Klass Pointer)、数组长度(不一定有),在64位Java虚拟机里面的Mark word  包含了我们的 hashcode的值  我们的分代年龄 锁标志位等
  • 实例数据 并不是所有的变量都存放在这里,对象的的所有成员变量以及其父类的成员变量是存放在这里的。
  • JVM要求Java对象的大小必须是8byte的倍数,所以这个的作用就是把对象的大小补齐至8byte的倍数。


那你说说Synchronized锁升级的过程吧

锁级别从低到高依次是:

  • 无锁状态
  • 偏向锁状态
  • 轻量级锁状态
  • 重量级锁状态

小六六在这给大家明确的一点就是,Synchronized锁的是对象而不是其包裹的代码。

  • 对象被new出来后,没有任何线程持有这个对象的锁,这时就是无锁状态;Mark Word锁标识是01
  • 当且仅当只有一个线程A获取到这个对象的锁的时候,对象就会从无锁状态升级成为偏向锁,Mark Word中就会记录这个线程的标识(锁标识是01),此时线程A持有这个对象的锁;
  • 还是这个线程A再次获取这个对象的锁时,发现他是一个偏向锁,并且对象头中记录着自己的线程标识,那么线程A就继续使用这把锁(不需要cas去获取锁了)。这里也是锁的可重入性,所以,synchronized也是可重入锁;
  • 在线程A持有锁的时候,线程B也来争抢这把锁了,线程B发现这是一把偏向锁,并且对象头中的线程标识不是自己。那么首先进行偏向锁的撤销过程,然后偏向锁就会升级为轻量级锁,此时Mark Word 的锁标识是00
  • 又有一些线程来争抢这个轻量级锁了,争抢的过程其实就是利用CAS自旋。为了避免长时间自旋消耗CPU资源,当自旋超过10次的时候,轻量级锁升级为重量级锁(其他线程阻塞,不会耗费cpu)。此时Mark Word的锁标识是10


可以聊聊CAS吗,它有什么问题吗?

CAS,compare and swap的缩写,就是一个保证原子性的一个手段,CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。


问题

  • ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
  • 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
  • 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。


可以说说Synchronized和Lock的区别嘛

  • 首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
  • synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  • synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可中断、可公平(两者皆可)
  • synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
  • 而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。


既然提到了Lock,那我们来聊聊他最常用的实现ReentrantLock吧,说说的公平和非公平是怎么实现的,他们的哪个效率高,默认是哪个,又是怎么现实可重入的

  • 首先公平和非公平是指多线程下各线程获取锁的顺序,先到的线程优先获取锁,而非公平锁则无法提供这个保障,但是呢 我们知道ReentrantLock的非公平实现,其实并不是随机的,它是有一定顺序的非公平,举个非公平的例子,假设A来获取锁,如果A获得了锁,此时B来获取锁,然后B失败了,B就去队列等待,此时C来了,然后C也失败了,他也去等待,此时D过来了,然后A释放了锁,那你说如果是绝对公平的话这个时候应该是B获取锁才对,但是源码中是D此时有机会去获取锁,所以它是一定顺序的非公平,非公平锁效率高于公平锁的,因为非公平锁减少了线程挂起的几率,后来的线程有一定几率逃离被挂起的开销,所以默认是非公平的锁。而我们的Synchronized也是非公平的
  • 可重入是指,当我一个线程获取了这把锁,下次当前线程再释放锁之前再去获取锁的时候是可以成功,这就是可重入锁,Lock 的实现是判断是当前线程的时候,给锁状态+1,然后我们的Synchronized也是可重入的
相关文章
|
2月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
213 60
【Java并发】【线程池】带你从0-1入门线程池
|
21天前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
56 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
27天前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
1月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
100 23
|
27天前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
|
1月前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
159 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
1月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
148 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
21天前
|
前端开发 JavaScript 关系型数据库
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
58 5
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
|
4月前
|
存储 缓存 负载均衡
后端开发中的性能优化策略
本文将探讨几种常见的后端性能优化策略,包括代码层面的优化、数据库查询优化、缓存机制的应用以及负载均衡的实现。通过这些方法,开发者可以显著提升系统的响应速度和处理能力,从而提供更好的用户体验。
144 6
|
18天前
|
监控 前端开发 小程序
陪练,代练,护航,代打小程序源码/前端UNIAPP-VUE2.0开发 后端Thinkphp6管理/具备家政服务的综合型平台
这款APP通过技术创新,将代练、家政、娱乐社交等场景融合,打造“全能型生活服务生态圈”。以代练为切入点,提供模块化代码支持快速搭建平台,结合智能匹配与技能审核机制,拓展家政服务和商业管理功能。技术架构具备高安全性和扩展性,支持多业务复用,如押金冻结、录屏监控等功能跨领域应用。商业模式多元,包括交易抽成、增值服务及广告联名,同时设计跨领域积分体系提升用户粘性,实现生态共生与B端赋能。
82 11

热门文章

最新文章

下一篇
oss创建bucket