前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java面试题 --- 并发①

java面试题 --- 并发①

作者头像
贪挽懒月
发布2022-05-13 15:03:39
2260
发布2022-05-13 15:03:39
举报
文章被收录于专栏:JavaEEJavaEE

1. 什么叫死锁?

  • 死锁就是多个线程对自己持有的资源不释放,同时又去申请对方持有的资源,形成循环等待。

2. 什么是乐观锁?什么是悲观锁?

  • 乐观锁就是很乐观,认为别人不会修改它的数据,不会上锁,每次操作前会判断别人有没有改过它的数据,CAS 就属于乐观锁;
  • 悲观锁就是很悲观,认为别人一定会改它的数据,每次操作都会上锁,Synchronized 和 ReentrantLock 都是悲观锁。

3. 什么是自旋锁?适应性自旋锁呢

  • 阻塞和唤醒线程需要系统切换CPU状态,这个开销是比较大的。如果一段代码的执行时间很短,刚阻塞线程,代码就执行完了,接着又唤醒线程,这种方式效率不高,倒不如让线程不阻塞,等代码执行完。那么就可以让线程自旋,不必阻塞,等前面线程执行完它就可以获取资源,这就是自旋锁。实现原理就是 CAS。适应性自旋锁就是虚拟机会判断自旋获取到锁的概率大不大,如果大,就自旋,如果不大,就阻塞线程。

4. 什么是共享锁?什么是独占锁?

  • 共享锁就是可以同时被多个线程持有,一个资源被加上共享锁后,那么其他线程也只能对其加共享锁。获得共享锁的线程只能读数据,不能写数据。ReentrantReadWriteLock 的读锁是共享锁,写锁是独占锁。
  • 独占锁又叫排他锁,同一时刻只能被一个线程持有,获取到锁的线程可以对资源进行读写操作。Synchronized 和 ReentrantLock 就是独占锁。

5. 什么是可重入锁?

  • 可重入锁又叫递归锁,就是一个线程获取到锁后,就可以进入它同步着的所有代码,即使内层函数也被锁住,也无需重新获取锁,Synchronized 和 ReentrantLock 都是可重入锁。

6. 可重入锁的原理是什么?

  • 有个 state 变量,线程获取到锁就加 1,释放锁就减 1,线程获取锁的时候会判断 state 是不是 0,是 0 就让线程获取锁,state 加 1,如果不是 0,但是当前持有锁的线程等于当前线程,state 也会加 1。

7. 公平锁和非公平锁有什么区别?

  • 公平锁就是按照申请锁的顺序去获取锁,先来后到,而非公平锁并不会按照顺序,在高并发的情况下可能出现优先级反转和饥饿现象,就是优先级高的反而后获取到锁,或者某些线程一直没有获取到锁。Synchronized 就是非公平锁,ReentrantLock 可以默认非公平锁,可以通过参数设置为公平锁。

8. CAS 是什么,会有什么问题吗?怎么解决?

  • CAS 就是比较并交换,它有三个操作数,内存值,期望值,更新值。当且仅当内存值等于期望值时,才会把内存值修改为更新值,它以自旋的方式同步线程,减少了线程切换的开销。它会出现 ABA 问题,就是线程 1 将共享变量 A 改为 B,再改为 A,线程 2 去判断的时候,以为没有别的线程改过,解决办法是可以每次操作都加个版本号。还有个问题就是 CAS 只能保证一个变量的原子操作,可以用原子引用来解决。如果 CAS 长时间不成功,就会一直自旋,占用大量的 CPU,可以加次数限制。CAS 底层是通过内存偏移量来获取内存值的。

9. 说说你对 AQS 的理解?

  • AQS,又叫抽象队列同步器,它是 JUC 的基石。JUC 包下的锁、并发工具,都有一些相似的代码,然后把这些代码抽取出来,就是 AQS,也即 JUC 包下的都是基于 AQS 去构建的。

10. AQS 是怎么协调工作的?

  • AQS 底层是通过 LockSupport 实现的。LockSupport 是等待唤醒的另一种实现方式,它使用一个通行证的概念。每个线程都有一个通行证,通行证的数量只可能为 0 或者 1,默认是 0。调用 unpark 方法的时候会发放一个通行证,线程就会被唤醒;调用 park 方法的时候,就会消耗一个通行证,线程就会被阻塞。park 和 unpark 方法底层是调用了 unsafe 类的 native 方法。

11. AQS 的工作原理是什么?

  • 有个 volatile 修饰的 int 类型的 state 变量,用来表示同步状态,将线程封装成 node 节点,通过内置的队列来完成资源的获取和排队工作,用 CAS 来完成对状态的修改。

12. 线程申请资源的时候,AQS 是怎么进行入队和出队工作的?

  • 入队:假如现在线程 A 正持有锁,此时线程 B 想获取锁,就要进入到队列中等待。如果队列还是空的,首先会创建一个节点,称为傀儡节点,然后把队列的 head 指针和 tail 指针都指向它,然后把线程 B 封装成一个节点,然后把这个节点的 prev 指向傀儡节点,把傀儡节点的 next 指向该节点,最后把 tail 指针指向该节点。
  • 出队:假如现在线程 A 释放锁了,那么线程 B 对应的节点就应该出队。首先会把 head 指向线程 B 对应的节点,然后把线程 B 对应节点的线程设置为空,接着把该节点的 prev 设置为空,把傀儡节点的 next 设置为空,这样一来,原先线程 B 所在的节点就成了新的傀儡节点,原先的傀儡节点就没有任何引用指向它,就会被 GC 回收。

13. 你知道哪些并发关键字?

  • synchronized,volatile、final。

14. 说说你对 synchronized 的理解?

  • 它是一个关键字,是可重入的非公平锁。可以修饰实例方法、静态方法和代码块。修饰实例方法时锁对象是实例,修饰静态方法时锁对象是当前类,修饰代码块时锁对象可以任意对象。修饰方法时,通过 javap 命令反汇编可以看到它是通过 ACC_SYNCHRONIZED 标识来实现同步的;而修饰代码块时是通过 monitor 对象来实现同步的,monitorenter 指向锁开始的地方,monitorexist 指向锁退出的地方,并且有两个 monitorexist,是为了防止程序异常导致锁未释放。

15. 锁状态有哪些? 无锁、偏向锁、轻量级锁、重量级锁。

  • 无锁就是不阻塞线程,在循环内不断地尝试,CAS 便是无锁的实现;
  • 偏向锁就是在锁对象头里会保存当前持锁的线程 ID,如果申请资源的线程 ID 等于对象头里保存的线程 ID,那就直接让线程获取锁;
  • 轻量级锁就是当锁是偏向锁时别的线程进来请求资源了,那就会自旋一定次数去尝试获取锁,自旋一定次数没获取到,又或者是一个线程持有锁,一个在自旋,第三个线程进来了,都会升级为重量级锁;
  • 重量级锁就是等待锁的线程都会阻塞。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档