JavaEE——死锁

发布于:2025-07-18 ⋅ 阅读:(18) ⋅ 点赞:(0)

前言

死锁是在开发中经常会遇到的一个问题,指的是多个进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象。

1. 死锁的成因

死锁的产生必须要同时满足以下四个条件:

  1. 互斥条件(Mutual Exclusion)
    资源一次只能由一个线程占用,其他线程必须等待该资源释放,不能使用。

  2. 占有并等待(Hold and Wait)
    线程已经持有至少一个资源,并且正在等待获取其他被占用的资源。

  3. 非抢占条件(No Preemption)
    已分配给进程的资源不能被其他线程强行夺取,必须由线程自行释放。

  4. 循环等待(Circular Wait)
    存在一个线程等待的循环链,每个进程都在等待下一个进程所占用的资源。

以下是一个典型的死锁案例:

    
    public static void main(String[] args) {
        //死锁案例
        Object lock1 = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("线程1获取到锁1,等待锁2");
                try {
                    Thread.sleep(100); // 确保线程2能获取到锁2
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("线程1获取到锁2");
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程2获取到锁2,等待锁1");
                try {
                    Thread.sleep(100); // 确保线程1能获取到锁1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("线程2获取到锁1");
                }
            }
        });
    }

这四个条件同时成立,就会形成死锁,当我们打破其中任意一个条件,死锁就会消失。

2. 解决死锁问题

上述提到的四个条件,前三个条件都不好打破,第四个是我们最容易破坏的条件,所以为了解决死锁问题,我们最常用的方法就是破坏”循环等待“。

为了破坏这个条件,我们使用锁排序,我们可以把所有的锁进行编号,规定线程按照固定的编号顺序来获取锁,这样就可以避免环路的产生。

就如上面展示的死锁代码,我们把获取锁的顺序进行修改,就可以防止死锁的产生:

        //死锁案例
        Object lock1 = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    Thread.sleep(100); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("线程1获取到锁2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    Thread.sleep(100); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("线程2获取到锁1");
                }
            }
        });
    }

这里统一了线程对锁的获取顺序,组织了等待环路的产生。

总结

本篇文章简单的介绍了我们常常遇到的死锁问题,讲述了死锁的成因及其解决方法,希望通过这篇文章,能够加深你对死锁的理解,能够轻松的发现并应对可能产生的死锁问题。


网站公告

今日签到

点亮在社区的每一天
去签到