Java多线程面试问题总结

这篇博文总结一些面试之中Java多线程的一些关键问题。每一个问题,会简要介绍,一些问题,下面会附上详细的介绍博文链接,方便小伙伴们更好的学习和交流。谢谢大家。

目录

CAS

  Compare and Swap ,也就是比较和替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B。当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。
  CAS操作在底层是由C++来实现,调用了Atomic::cmpxchg方法。在这个方法内,首先通过一个内联函数来返回一个变量mp,mp用于判断是否为系统是否为多处理器。如果是多核的,那么mp=1,否则mp=0。紧接着,如果mp=1,则为cmpxchg指令添加Lock前缀,否则不添加。
  Lock前缀可以保证对变量的读-修改-写的操作时原子性的。


ABA问题

  因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

  ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A-B-A 就会变成1A-2B-3A。

AQS

  抽象队列同步器。JUC的核心就是AQS-AbstractQueuedSynchronizer。在内部,以一个双向队列维护所有的Entry。例如,在ReetrantLock中,所有等待获取锁的线程都被放在一个Entry中连接成双向队列。
  参考博文: Java并发编程基础—(9)Java中的锁—队列同步器


自旋锁

  由Synchronized修饰的代码块,在一个线程进入代码块后,其他线程必须在外等待进入阻塞状态。在很大程度上降低了程序运行的效率。如果,Synchronized修饰的代码块运行速度较快,那么可以尝试让其他等待锁的线程在Synchronized做忙循环,也就是自旋。如果做了多次忙循环,仍然没有获得锁,再进入阻塞状态。
  自旋锁适用于共享数据的锁定状态很短的场景。


volatile关键字

  • 可见性 :对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写。
  • 原子性:对单个volatile变量的读/写具体原子性,但对于Volatile++这样的操作(写+赋值,本质上是两步操作)不具有原子性。

内存语义分析
  volatile关键字能保证可见性的原因是,假设现在有线程A和线程B,线程A对volatile边;1进行修改,然后线程B读一个volatile变量时,JVM会把线程B的本地内存置为无效,接下来去共享内存中读取共享变量。


Synchronized的实现

  Synchronized可以保证同步代码块在某个时间内,只有一个线程进入,其他线程在队列中阻塞,等待。那么在底层是如何实现的呢?
  在底层,对于同步代码块使用了monitorenter(监视器进入)和monitorexit(监视器退出)指令。对于Synchronized修饰的同步方法,使用了ACC_SYNCHRONIZED。本质上,是对一个对象的监视器(monitor)进行获取,而且这个获取的过程是互斥的,在某一时刻只能有一个线程获取到由Synchronized保护的对象监视器。
  只有获得了这个监视器才可以进入同步代码块或者同步方法,未获得的线程被阻塞在同步代码块或者同步方法的入口处,进入BLOCKED(阻塞)状态。


时间片

  时间片就是指CPU分配给每个线程的时间,因为时间片非常短,所以通过不停地切换线程执行,让人感觉程序是是在并行执行。


上下文切换

  由于CPU通过时间片分配算法来并行执行任务,每个任务在执行指定的时间片后,会保存自己当前的状态。而任务从保存到下次加载(再次获得时间片)的过程就是一次上下文切换。


锁消除

  锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。

  锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。


锁消除

  锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。

  锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。


锁粗化

  对于如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。
  如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。


轻量级锁

  轻量级锁是相对于传统的重量级锁而言,它使用 CAS 操作来避免重量级锁使用互斥量的开销。对于绝大部分的锁,在整个同步周期内都是不存在竞争的,因此也就不需要都使用互斥量进行同步,可以先采用 CAS 操作进行同步,如果 CAS 失败了再改用互斥量进行同步。


偏向锁

  偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。
  当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定状态或者轻量级锁状态。


博客参考

(1):https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E5%B9%B6%E5%8F%91.md#%E5%8D%81%E4%BA%8C%E9%94%81%E4%BC%98%E5%8C%96

(2):https://blog.csdn.net/v123411739/article/details/79561458


不断更新中。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值