死锁的定义、危害、发生死锁的四个必要提交以及代码演示

it2023-09-09  82

1、什么是死锁

前提:首先死锁一定是发生在并发中,在并发中我们为了保证线程安全,会使用一些加锁、信号量的方法,但是使用不当的情况下就造成了死锁。

当两个(或者更多)线程(或进程)互相持有对方需要的资源,但又不主动释放,导致所有人都无法继续前进,导致程序陷入无尽的阻塞,这就是死锁。

两个线程发生死锁: 多个线程发生死锁:如果多个线程之间的依赖关系是环形,存在环路的锁依赖关系,那么也可能会发生死锁。

2、死锁的影响

死锁的影响在不同的系统中是不一样的,这取决于系统对死锁的处理能力。 比如:

在数据库中: 存在检测并放弃事务:据库如果检测到了两个事务发生死锁,那么会随机指定一个事务,放弃。等待另一个事务执行完成之后再执行。 这样即便发生了死锁也会被数据库自己解决,不会对我们的程序造成太大的影响JVM中: 无法像数据库中一样提供自动处理。不是不能提供,而是因为我们的业务时常变化且复杂的。JVM不知道如何对我们的业务进行取舍,因此将死锁的处理任务交给程序员去解决,不过它可以提供死锁的检查功能(jstack、ThreadMXBean)。

3、死锁的危害

死锁并不是不一定发生,但是遵守”墨菲定律“。(几率很小,但是随着时间的推移,发生的可能性会增大)一旦发生,多是高并发场景,影响用户多整个系统崩溃,子系统崩溃,性能降低压力测试无法找出所有潜在的死锁

4、发生死锁的四个必要条件

1、互斥条件(对资源的操作是线程独享的)

2、请求与保持(已经获取的资源不释放)

3、不剥夺条件(数据库利用这个条件避免死锁)

4、循环等待条件(多个线程)

5、代码演示死锁

模拟两个线程相互持有对方资源,却无法主动释放产生死锁的情况,这也是最为常见的情况

public class MustDeadLock implements Runnable{ int flag = 0; static Object o1 = new Object(); static Object o2 = new Object(); public static void main(String[] args) { MustDeadLock mustDeadLock1 = new MustDeadLock(); MustDeadLock mustDeadLock2 = new MustDeadLock(); mustDeadLock1.flag = 0; mustDeadLock2.flag = 1; new Thread(mustDeadLock1).start(); new Thread(mustDeadLock2).start(); } @Override public void run() { System.out.println("flag = " + flag); if (flag == 0) { synchronized (o1){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ System.out.println("线程1成功获取到了两把锁"); } } } if (flag == 1) { synchronized (o2){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("线程2成功获取到了两把锁"); } } } } }

运行结果:

原因分析:线程1获取到了o1,等待o2;与此同时线程2持有了o2,等待获取o1。两个线程互相等待对方持有的资源,都不肯释放,导致死锁发生!

最新回复(0)