文末
我将这三次阿里面试的题目全部分专题整理出来,并附带上详细的答案解析,生成了一份PDF文档
- 第一个要分享给大家的就是算法和数据结构
- 第二个就是数据库的高频知识点与性能优化
- 第三个则是并发编程(72个知识点学习)
- 最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料
还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来
-
代码实现一个必然死锁的示例
-
分析死锁的过程
1.什么是死锁?
关键词:并发场景,多线程
首先我们需要知道,死锁一定发生在并发场景中。我们为了保证线程安全,有时会给程序使用各种能保证并发安全的工具,尤其是锁,但是如果在使用过程中处理不得当,就有可能会导致发生死锁的情况。
关键词:互不相让
死锁是一种状态,当两个(或多个)线程(或进程)相互持有对方所需要的资源,却又都不主动释放自己手中所持有的资源,导致大家都获取不到自己想要的资源,所有相关的线程(或进程)都无法继续往下执行,在未改变这种状态之前都不能向前推进,我们就把这种状态称为死锁状态,认为它们发生了死锁。
简而言之,死锁就是两个或多个线程(或进程)被无限期地阻塞,相互等待对方手中资源的一种状态。
两个线程死锁的情况
如图所示,线程1 已经持有了 锁1,同时 线程2 也已经持有了锁2,然后 线程1 尝试获取 锁2,但是 线程2 并没有释放 锁2,所以 线程1 处于阻塞状态,同理可知,图中的 线程2 获取 锁1也会被阻塞。
这样一来,线程1 和 线程2 就发生了死锁,因为它们都相互持有对方想要的资源,却又不释放自己手中的资源,形成相互等待,而且会一直等待下去。
2.死锁的影响和危害
2.1 死锁的影响
死锁的影响在不同系统中是不一样的,影响的大小一部分取决于当前这个系统或者环境对死锁的处理能力。
2.1.1 数据库中
例如,在数据库系统软件的设计中,考虑了监测死锁以及从死锁中恢复的情况。在执行一个事务的时候可能需要获取多把锁,并一直持有这些锁直到事务完成。在某个事务中持有的锁可能在其他事务中也需要,因此在两个事务之间有可能发生死锁的情况,一旦发生了死锁,如果没有外部干涉,那么两个事务就会永远的等待下去。
但数据库系统不会放任这种情况发生,当数据库检测到这一组事务发生了死锁时,根据策略的不同,可能会选择放弃某一个事务,被放弃的事务就会释放掉它所持有的锁,从而使其他的事务继续顺利进行。
此时程序可以重新执行被强行终止的事务,而这个事务现在就可以顺利执行了,因为所有跟它竞争资源的事务都已经在刚才执行完毕,并且释放资源了。
2.1.2 JVM 中
在 JVM 中,对于死锁的处理能力就不如数据库那么强大了。如果在 JVM 中发生了死锁,JVM 并不会自动进行处理,所以一旦死锁发生,就会陷入无穷的等待。
2.2 死锁的危害以及特点
关键词:概率性事件
死锁的问题和其他的并发安全问题一样,是概率性的,也就是说,即使存在发生死锁的可能性,也并不是 100% 会发生的。如果每个锁的持有时间很短,那么发生冲突的概率就很低,所以死锁发生的概率也很低。但是在线上系统里,可能每天有几千万次的“获取锁”、“释放锁”操作,在巨量的次数面前,整个系统发生问题的几率就会被放大,只要有某几次操作是有风险的,就可能会导致死锁的发生。
也正是因为死锁“不一定会发生”的特点,导致提前找出死锁成为了一个难题。压力测试虽然可以检测出一部分可能发生死锁的情况,但是并不足以完全模拟真实、长期运行的场景,因此没有办法把所有潜在可能发生死锁的代码都找出来。
关键词:危害大,发生几率不高
一旦发生了死锁,根据发生死锁的线程的职责不同,就可能会造成 子系统崩溃、性能降低 甚至 整个系统崩溃 等各种不良后果。而且死锁往往发生在高并发、高负载的情况下,因为可能会直接影响到很多用户,造成一系列的问题。以上就是死锁发生几率不高但是危害大的特点。
3.必然死锁示例
public class MustDeadLockDemo {
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
new Thread(new DeadLockTask(lock1, lock2, true), “线程1”).start();
new Thread(new DeadLockTask(lock1, lock2, false), “线程2”).start();
}
static class DeadLockTask implements Runnable {
private boolean flag;
private Object lock1;
private Object lock2;
public DeadLockTask(Object lock1, Object lock2, boolean flag) {
this.lock1 = lock1;
this.lock2 = lock2;
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + “->拿到锁1”);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “->等待锁2释放…”);
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + “->拿到锁2”);
}
}
}
if (!flag) {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + “->拿到锁2”);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “->等待锁1释放…”);
读者福利
分享一份自己整理好的Java面试手册,还有一些面试题pdf
不要停下自己学习的脚步
(img-ukXQuCRE-1715686245724)]