Android并发编程高级面试题汇总(含详细解析 七)
  CpwfxCg9mmk0 2023年11月22日 13 0

Android并发编程高级面试题汇总最全最细面试题讲解持续更新中👊👊 👀你想要的面试题这里都有👀 👇👇👇

死锁的场景和解决方案 腾讯

这道题想考察什么?

是否真正了解死锁的定义?是否掌握死锁的排查与解决

考察的知识点

并发编程 死锁

考生应该如何回答

死锁的定义

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。

危害

1、线程不工作了,但是整个程序还是活着的

2、没有任何的异常信息可以供我们检查。

3、一旦程序发生了发生了死锁,是没有任何的办法恢复的,只能重启程序,对正式已发布程序来说,这是个很严重的问题。

死锁的发生必须具备以下四个必要条件。

  1. 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  2. 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
  3. 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
  4. 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。只要打破四个必要条件之一就能有效预防死锁的发生。

  1. 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
  2. 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
  3. 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
  4. 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

避免死锁常见的算法有:有序资源分配法银行家算法等。

有序资源分配法

有序资源分配法是预防死锁的一种算法,按某种规则对系统中的所有资源统一编号,申请时必须以上升的次序。

例如做饭时候盐为1,酱油为2等等。如果A、B两个厨师同时做饭,使用资源顺序分别为:

A:申请顺序1->2

B:申请顺序2->1

此时,A在拿着盐的同时要使用酱油,但是由于酱油被B持有,两人谁也不让谁。此时形成环路条件,造成死锁 。但是采用有序资源分配法,则:

A:申请顺序1->2

B:申请顺序1->2

A如果先获取到盐,那么B此时只能等待。这样就破坏了环路条件,避免了死锁的发生。

总结

死锁是必然发生在多操作者(M>=2个)情况下,争夺多个资源(N>=2个,且N<=M)才会发生这种情况。很明显,单线程自然不会有死锁,只有B一个去,不要2个,打十个都没问题;单资源呢?只有13,A和B也只会产生激烈竞争,打得不可开交,谁抢到就是谁的,但不会产生死锁。

4.12 锁分哪几类?

详细讲解

享学课堂移动互联网系统课程:架构师筑基必备技能《线程与进程的理论知识入门1》

这道题想考察什么?

是否了解并发相关锁的知识?

考察的知识点

  1. 锁的分类和概念
  2. 如何运用锁解决并发问题

考生应该如何回答

Java锁的种类
  • 乐观锁/悲观锁
  • 独享锁/共享锁
  • 互斥锁/读写锁
  • 可重入锁
  • 公平锁/非公平锁
  • 分段锁
  • 偏向锁/轻量级锁/重量级锁
  • 自旋锁

以上是一些锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计。

乐观锁/悲观锁

乐观锁与悲观锁并不是特指某两种类型的锁,是人们定义出来的概念或思想,主要是指看待并发同步的角度。

  • 乐观锁:获取数据时认为不会被其他线程修改,所以不会上锁,但是在更新的时候会判断其他线程是否修改此数据,如果被其他线程修改,则会发生自旋。
  • 悲观锁:总是假设最坏的情况,获取数据时都认为其他线程会修改,因此在获取数据时都会上锁,这样保证其他线程需要等待获取锁的线程处理完成并且释放锁。

乐观锁适用于频繁读取的场景,因为不会上锁,因此可以提高吞吐量。在Java中java.util.concurrent.atomic包下面的原子变量类就是基于乐观锁的一种实现方式CAS(Compare and Swap 比较并交换)实现的。

悲观锁适合写操作较多的场景,synchronized关键字的实现就是悲观锁。

独享锁/共享锁
  • 独享锁是指该锁一次只能被一个线程所持有。
  • 共享锁是指该锁可被多个线程所持有。

ReentrantLock是独享锁。但是对于Lock的另一个实现读写锁ReadWriteLock,读锁是共享锁,而写锁则是独享锁。

互斥锁/读写锁

上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

  • 互斥锁在Java中的具体实现就是ReentrantLock。
  • 读写锁在Java中的具体实现就是ReadWriteLock。
可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。synchronized与ReetrantLock都是可重入锁。可重入锁的一个好处就是可以在一定程度避免死锁:

synchronized void setA() throws Exception{
	Thread.sleep(1000);
	setB();
}
    
synchronized void setB() throws Exception{
	Thread.sleep(1000);
}

上述代码中,如果synchronized不是可重入锁的话,setA首先获取锁,在此方法还未释放锁的情况下,调用setB也需要获取相同的对象锁,此时会造成死锁。

公平锁/非公平锁

公平锁是指多个线程按照申请锁的顺序获取锁,非公平锁则是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。非公平锁的优点在于吞吐量比公平锁大,但是也有可能会造成优先级反转或者饥饿现象。

Java中ReetrantLock可以通过构造函数指定该锁是否是公平锁,默认是非公平锁。而synchronized则是非公平锁。

分段锁

分段锁其实是一种锁的设计,并不是具体的一种锁。比如ConcurrentHashMap,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

ConcurrentHashMap中的分段锁封装为Segment,它本身也是类似于HashMap的结构,其内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

当需要put元素的时候,并不是对整个HashMap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。

自旋锁

在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

Android并发编程高级面试题汇总(含详细解析 七)_公平锁

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月22日 0

暂无评论

CpwfxCg9mmk0