2023Java高频必背并发编程面试题02

这篇具有很好参考价值的文章主要介绍了2023Java高频必背并发编程面试题02。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、ABA问题及解决⽅法简述。

CAS 算法是基于值来做⽐较的,如果当前有两个线程,⼀个线程将变量值从 A 改为 B ,再由 B 改回为 A,当前线程开始执⾏ CAS 算法时,就很容易认为值没有变化,误认为读取数据到执⾏ CAS 算法的期间,没有线程修改过数据。
juc 包提供了⼀个 AtomicStampedReference,即在原始的版本下加⼊版本号戳,解决 ABA 问题。

2、简述常⻅的Atomic类。

在很多时候,我们需要的仅仅是⼀个简单的、⾼效的、线程安全的++或者–⽅案,使⽤synchronized关键字和lock固然可以实现,但代价⽐较⼤,此时⽤原⼦类更加⽅便。
基本数据类型的原⼦类有
(1)AtomicInteger 原⼦更新整形;
(2)AtomicLong 原⼦更新⻓整型;
(3)AtomicBoolean 原⼦更新布尔类型。
Atomic数组类型有
(1)AtomicIntegerArray 原⼦更新整形数组⾥的元素;
(2)AtomicLongArray 原⼦更新⻓整型数组⾥的元素;
(3)AtomicReferenceArray 原⼦更新引⽤类型数组⾥的元素。
Atomic引⽤类型有
(1)AtomicReference 原⼦更新引⽤类型;
(2)AtomicMarkableReference 原⼦更新带有标记位的引⽤类型,可以绑定⼀个 boolean 标记;
(3)AtomicStampedReference 原⼦更新带有版本号的引⽤类型。
FieldUpdater类型
(1)AtomicIntegerFieldUpdater 原⼦更新整形字段的更新器;
(2)AtomicLongFieldUpdater 原⼦更新⻓整形字段的更新器;
(3)AtomicReferenceFieldUpdater 原⼦更新引⽤类型字段的更新器。

3、简述Atomic类基本实现原理。

以AtomicIntger 为例。⽅法getAndIncrement,以原⼦⽅式将当前的值加1,具体实现为:
(1)在 for 死循环中取得 AtomicInteger ⾥存储的数值
(2)对 AtomicInteger 当前的值加 1
(3)调⽤ compareAndSet ⽅法进⾏原⼦更新
(4)先检查当前数值是否等于 expect
(5)如果等于则说明当前值没有被其他线程修改,则将值更新为 next,
(6)如果不是会更新失败返回 false,程序会进⼊ for 循环重新进⾏ compareAndSet 操作。

4、简述CountDownLatch。

CountDownLatch这个类使⼀个线程等待其他线程各⾃执⾏完毕后再执⾏。是通过⼀个计数器来实现的,计数器的初始值是线程的数量。每当⼀个线程执⾏完毕后,调⽤countDown⽅法,计数器的值就减1,当计数器的值为0时,表示所有线程都执⾏完毕,然后在等待的线程就可以恢复⼯作了。只能⼀次性使⽤,不能reset。

5、简述CyclicBarrier。

CyclicBarrier 主要功能和CountDownLatch类似,也是通过⼀个计数器,使⼀个线程等待其他线程各⾃执⾏完毕后再执⾏。但是其可以重复使⽤(reset)。

6、简述Semaphore。

Semaphore即信号量。Semaphore 的构造⽅法参数接收⼀个 int 值,设置⼀个计数器,表示可⽤的许可数量即最⼤并发数。使⽤ acquire ⽅法获得⼀个许可证,计数器减⼀,使⽤ release ⽅法归还许可,计数器加⼀。如果此时计数器值为0,线程进⼊休眠。

7、简述Exchanger。

Exchanger类可⽤于两个线程之间交换信息。可简单地将Exchanger对象理解为⼀个包含两个格⼦的容器,通过exchanger⽅法可以向两个格⼦中填充信息。线程通过exchange ⽅法交换数据,第⼀个线程执⾏exchange ⽅法后会阻塞等待第⼆个线程执⾏该⽅法。当两个线程都到达同步点时这两个线程就可以交换数据当两个格⼦中的均被填充时,该对象会⾃动将两个格⼦的信息交换,然后返回给线程,从⽽实现两个线程的信息交换。

8、简述ConcurrentHashMap。

JDK7采⽤锁分段技术。⾸先将数据分成 Segment 数据段,然后给每⼀个数据段配⼀把锁,当⼀个线程占⽤锁访问其中⼀个段的数据时,其他段的数据也能被其他线程访问。
get 除读到空值不需要加锁。该⽅法先经过⼀次再散列,再⽤这个散列值通过散列运算定位到 Segment,最后通过散列算法定位到元素。put 须加锁,⾸先定位到 Segment,然后进⾏插⼊操作,第⼀步判断是否需要对 Segment ⾥的 HashEntry 数组进⾏扩容,第⼆步定位添加元素的位置,然后将其放⼊数组。
JDK8的改进
(1)取消分段锁机制,采⽤CAS算法进⾏值的设置,如果CAS失败再使⽤ synchronized 加锁添加元素;
(2)引⼊红⿊树结构,当某个槽内的元素个数超过8且 Node数组 容量⼤于 64 时,链表转为红⿊树;
(3)使⽤了更加优化的⽅式统计集合内的元素数量。

9、synchronized底层实现原理。

Java 对象底层都会关联⼀个 monitor,使⽤ synchronized 时 JVM 会根据使⽤环境找到对象的 monitor,根据 monitor 的状态进⾏加解锁的判断。如果成功加锁就成为该 monitor 的唯⼀持有者,monitor 在被释放前不能再被其他线程获取。
synchronized在JVM编译后会产⽣monitorenter 和 monitorexit 这两个字节码指令,获取和释放 monitor。
这两个字节码指令都需要⼀个引⽤类型的参数指明要锁定和解锁的对象,对于同步普通⽅法,锁是当前实例对象;对于静态同步⽅法,锁是当前类的 Class 对象;对于同步⽅法块,锁是synchronized 括号⾥的对象。
执⾏ monitorenter 指令时,⾸先尝试获取对象锁。如果这个对象没有被锁定,或当前线程已经持有锁,就把锁的计数器加 1,执⾏ monitorexit 指令时会将锁计数器减 1。⼀旦计数器为 0 锁随即就被释放。

10、synchronized关键词使⽤⽅法。

(1)直接修饰某个实例⽅法;
(2)直接修饰某个静态⽅法;
(3)修饰代码块。

11、简述Java偏向锁。

JDK 1.6 中提出了偏向锁的概念。该锁提出的原因是,开发者发现多数情况下锁并不存在竞争,⼀把锁往往是由同⼀个线程获得的。偏向锁并不会主动释放,这样每次偏向锁进⼊的时候都会判断该资源是否是偏向⾃⼰的,如果是偏向⾃⼰的则不需要进⾏额外的操作,直接可以进⼊同步操作。
其申请流程为
(1)⾸先需要判断对象的 Mark Word 是否属于偏向模式,如果不属于,那就进⼊轻量级锁判断逻辑。否则继续下⼀步判断;
(2)判断⽬前请求锁的线程 ID 是否和偏向锁本身记录的线程 ID ⼀致。如果⼀致,继续下⼀步的判断,如果不⼀致,跳转到步骤4;
(3)判断是否需要重偏向。如果不⽤的话,直接获得偏向锁;
(4)利⽤ CAS 算法将对象的 Mark Word 进⾏更改,使线程 ID 部分换成本线程 ID。如果更换成功,则重偏向完成,获得偏向锁。如果失败,则说明有多线程竞争,升级为轻量级锁。

12、简述轻量级锁。

轻量级锁是为了在没有竞争的前提下减少重量级锁出现并导致的性能消耗
其申请流程为:
(1)如果同步对象没有被锁定,虚拟机将在当前线程的栈帧中建⽴⼀个锁记录空间,存储锁对象⽬前 MarkWord 的拷⻉;
(2)虚拟机使⽤ CAS 尝试把对象的 Mark Word 更新为指向锁记录的指针;
(3)如果更新成功即代表该线程拥有了锁,锁标志位将转变为 00,表示处于轻量级锁定状态;
(4)如果更新失败就意味着⾄少存在⼀条线程与当前线程竞争。虚拟机检查对象的 Mark Word 是否指向当前线程的栈帧;
(5)如果指向当前线程的栈帧,说明当前线程已经拥有了锁,直接进⼊同步块继续执⾏;
(6)如果不是则说明锁对象已经被其他线程抢占;
(7)如果出现两条以上线程争⽤同⼀个锁,轻量级锁就不再有效,将膨胀为重量级锁,锁标志状态变为10,此时Mark Word 存储的就是指向重量级锁的指针,后⾯等待锁的线程也必须阻塞。

13、简述锁优化策略。

即⾃适应⾃旋、锁消除、锁粗化、锁升级等策略。

14、简述Java的⾃旋锁。

线程获取锁失败后,可以采⽤这样的策略,可以不放弃 CPU ,不停的重试内重试,这种操作也称为⾃旋锁。

15、简述⾃适应⾃旋锁。

⾃适应⾃旋锁⾃旋次数不再⼈为设定,通常由前⼀次在同⼀个锁上的⾃旋时间及锁的拥有者的状态决定。

16、简述锁粗化。

锁粗化的思想就是扩⼤加锁范围,避免反复的加锁和解锁。

17、简述锁消除。

锁消除是⼀种更为彻底的优化,在编译时,Java编译器对运⾏上下⽂进⾏扫描,去除不可能存在共享资源竞争的锁。

18、简述Lock与ReentrantLock。

Lock接⼝是 Java并发包的顶层接⼝。
可重⼊锁 ReentrantLock 是 Lock 最常⻅的实现,与 synchronized ⼀样可重⼊。ReentrantLock 在默认情况下是⾮公平的,可以通过构造⽅法指定公平锁。⼀旦使⽤了公平锁,性能会下降。

19、简述AQS。

AQS(AbstractQuenedSynchronizer)抽象的队列式同步器。AQS是将每⼀条请求共享资源的线程封装成⼀个锁队列的⼀个结点(Node),来实现锁的分配。AQS是⽤来构建锁或其他同步组件的基础框架,它使⽤⼀个 volatile int state 变量作为共享资源,如果线程获取资源失败,则进⼊同步队列等待;如果获取成功就执⾏临界区代码,释放资源时会通知同步队列中的等待线程。
⼦类通过继承同步器并实现它的抽象⽅法getState、setState 和 compareAndSetState对同步状态进⾏更改。
AQS获取独占锁/释放独占锁原理
(1)获取(acquire)
调⽤ tryAcquire ⽅法安全地获取线程同步状态,获取失败的线程会被构造同步节点并通过 addWaiter⽅法加⼊到同步队列的尾部,在队列中⾃旋;
调⽤ acquireQueued ⽅法使得该节点以死循环的⽅式获取同步状态,如果获取不到则阻塞。
(2)释放(release)
调⽤ tryRelease ⽅法释放同步状态;
调⽤ unparkSuccessor ⽅法唤醒头节点的后继节点,使后继节点重新尝试获取同步状态。
AQS获取共享锁/释放共享锁原理:
获取锁(acquireShared)
调⽤ tryAcquireShared ⽅法尝试获取同步状态,返回值不⼩于 0 表示能获取同步状态。
释放(releaseShared),并唤醒后续处于等待状态的节点。

20、死锁与活锁的区别,死锁与饥饿的区别?

死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成
的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生死锁的必要条件
(1)互斥条件:所谓互斥就是进程在某一时间内独占资源。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,
失败,尝试,失败。
活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而
处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执
行的状态。
Java 中导致饥饿的原因:
(1)高优先级线程吞噬所有的低优先级线程的 CPU 时间。
(2)线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前
持续地对该同步块进行访问。
(3)线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的 wait 方
法),因为其他线程总是被持续地获得唤醒。文章来源地址https://www.toymoban.com/news/detail-406525.html

到了这里,关于2023Java高频必背并发编程面试题02的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【面试】Java高频面试题(2023最新整理)

    JDK(Java Development Kit),Java开发工具包 JRE(Java Runtime Environment),Java运行环境 JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib 就是JVM工作所需要的类库。 对于基本类型,== 比较的是值; 对于引用类型,==比较的是地址; equals不能用于基

    2023年04月08日
    浏览(36)
  • 【面试】Java高频面试题(2023最新版)

    JDK(Java Development Kit),Java开发工具包 JRE(Java Runtime Environment),Java运行环境 JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib 就是JVM工作所需要的类库。 对于基本类型,== 比较的是值; 对于引用类型,==比较的是地址; equals不能用于基

    2023年04月26日
    浏览(38)
  • 2023 最新「阿里」Java 高级工程师面试高频题

    7月份快要过去了,也意味着金九银十快到了,不知道大家有没有发现今年的面试比之前的都难;去年因为疫情的原因压抑住程序员跳槽的想法,再加上现在 IT 行业内卷越来越严重,这两点原因导致今年的招聘市场非常难受; 最近群里很多小伙伴都在问,如何抓住银四的尾巴

    2024年02月13日
    浏览(28)
  • 【面试】java并发编程面试题

    java并发面试题 https://javaguide.cn/home.html java基础面试题 https://blog.csdn.net/jackfrued/article/details/44921941 java集合面试题 https://javaguide.cn/java/collection/java-collection-questions-01.html javaIO面试题 https://javaguide.cn/java/io/io-basis.html JVM面试题 https://javaguide.cn/java/jvm/jvm-garbage-collection.html 计算机网络

    2024年01月21日
    浏览(39)
  • 2023金三银四1000道java面试必考题(附答案,赶紧收藏)包含所有大厂高频面试知识点

    我的回答是: 很有必要 。你可以讨厌这种模式,但你一定要去背,因为不背你就进不了大厂。现如今,Java 面试的本质就是八股文,把八股文面试题背好,面试才有可能表现好。金九银十招聘黄金季即将来临!大家在考研和找工作中纠结的时候,不妨先看一下面试题,毕竟我

    2023年04月09日
    浏览(43)
  • Java并发编程面试题

    目录 一、线程、进程、程序 二、线程状态  三、线程的七大参数 四、线程有什么优缺点? 五、start 和 run 方法有什么区别? 六、wait 和 sleep的区别? 七、lock与synchronized的区别 八、Volatile是线程安全的吗?底层原理是什么? 九、synchronized作用和底层原理? 十一、Thre

    2024年02月12日
    浏览(49)
  • 01.java并发编程面试宝典

    谈谈什么是线程池 线程池和数据库连接池非常类似,可以统一管理和维护线程,减少没有必要的开销。 因为频繁的开启线程或者停止线程,线程需要从新被 cpu 从就绪到运行状态调度,需要发生上下文切换 实际开发项目中 禁止自己 new 线程。必须使用线程池来维护和创建线

    2024年02月03日
    浏览(92)
  • Java并发编程面试题——线程池

    参考文章: 《Java 并发编程的艺术》 7000 字 + 24 张图带你彻底弄懂线程池 (1) 线程池 (ThreadPool) 是一种用于 管理和复用线程的机制 ,它是在程序启动时就预先创建一定数量的线程,将这些线程放入一个池中,并对它们进行有效的管理和复用,从而在需要执行任务时,可以从

    2024年02月07日
    浏览(38)
  • Java面试_并发编程_线程基础

    进程是正在运行程序的实例, 进程中包含了线程, 每个线程执行不同的任务 不同的进程使用不同的内存空间, 在当前进程下的所有线程可以共享内存空间 线程更轻量, 线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程) 并发是单个

    2024年02月07日
    浏览(44)
  • 每日三道面试题之 Java并发编程 (一)

    并发编程是一种允许多个操作同时进行的编程技术,这种技术在现代软件开发中非常重要,原因如下: 充分利用多核处理器 :现代计算机通常都拥有多核处理器,通过并发编程,可以让每个核心独立执行不同的任务,从而显著提高程序的执行效率和吞吐量。 提高资源利用率

    2024年04月09日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包