• Java面试题:解释死锁的概念,给出避免死锁的常见策略。你能给我一个具体的例子吗?


    死锁(Deadlock)是多线程编程中的一种现象,指的是两个或多个线程永久性地阻塞,每个线程等待其他线程释放锁,但是这些锁又被其他线程持有,导致没有任何线程能够继续执行,从而导致程序无法前进。
    死锁通常发生在以下四个条件同时满足时:

    1. 互斥条件:资源不能被多个线程共同使用,只能由一个线程独占。
    2. 持有和等待条件:线程至少持有一个资源,并且正在等待获取额外的资源,而该资源又被其他线程持有。
    3. 非抢占条件:线程持有的资源在未使用完毕前不能被其他线程强行抢占。
    4. 循环等待条件:存在一个线程与资源之间的循环等待链,每个线程都在等待下一个线程所持有的资源。
      为了避免死锁,可以采取以下策略:
    5. 资源有序分配:规定所有线程按照相同的顺序请求资源,这样就可以避免循环等待链的形成。
    6. 一次性请求所有资源:线程在开始执行前一次性请求所有需要的资源,这样就可以避免持有部分资源而等待其他资源的情况。
    7. 超时等待:线程在请求资源时设置超时时间,如果在超时时间内没有获得资源,则放弃等待,回退并重新尝试。
    8. 资源预分配:预先分配一定量的资源给线程,减少线程在运行时对资源的动态请求。
    9. 锁排序:线程在请求锁时,按照一定的规则进行排序,确保锁的请求和释放顺序一致。
    10. 死锁检测和恢复:运行时检测死锁的发生,并采取措施解除死锁,例如撤销某个线程或回滚操作。
    11. 使用并发工具:使用Java并发API中的工具类,如ReentrantLock,它提供了尝试锁定和定时锁定,以及锁的可中断特性,这些都有助于避免死锁。
      通过合理的设计和编码实践,可以有效地避免死锁的发生,确保多线程程序的稳定性和可靠性。

    当然可以。以下是一个简单的Java示例,它展示了死锁是如何发生的:

    public class DeadlockDemo {
        private final Object lock1 = new Object();
        private final Object lock2 = new Object();
        public void method1() {
            synchronized (lock1) {
                System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and waiting for lock2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and lock2");
                }
            }
        }
        public void method2() {
            synchronized (lock2) {
                System.out.println("Thread " + Thread.currentThread().getId() + " holds lock2 and waiting for lock1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and lock2");
                }
            }
        }
        public static void main(String[] args) {
            DeadlockDemo demo = new DeadlockDemo();
            Thread t1 = new Thread(demo::method1);
            Thread t2 = new Thread(demo::method2);
            t1.start();
            t2.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这个例子中,我们有两个方法method1method2,它们各自尝试获取两个不同的锁。线程t1开始执行method1,它获得了lock1,然后尝试获取lock2。同时,线程t2开始执行method2,它获得了lock2,然后尝试获取lock1
    由于这两个方法中的锁请求都是先获得一个锁,然后尝试获取另一个锁,而且它们都没有设置超时,所以它们都会无限期地等待对方释放锁。这样,两个线程都持有所获得的锁,并且都在等待对方释放锁,导致了死锁的发生。
    运行这个程序,你可能会看到输出类似于以下内容:

    Thread 1 holds lock1 and waiting for lock2
    Thread 2 holds lock2 and waiting for lock1
    
    • 1
    • 2

    在这种情况下,程序将无法继续执行,因为两个线程都无法继续执行它们的操作,因为他们互相等待对方释放锁。
    要避免这种死锁,我们可以采取一些策略,比如设置锁请求的超时时间,或者确保线程在获取锁时的顺序一致。

  • 相关阅读:
    使用jspdf插件将网页中的内容导出为高保真的pdf文件
    车牌自动识别-matlab
    想要精通算法和SQL的成长之路 - 填充书架
    Java入门7-面向对象基础
    【Linux】进程控制
    一次Kafka内存泄露排查经过
    版本控制--Git
    高维统计理论 Gauss与Rademacher复杂度
    Python下载安装教程Python3.7版本
    读《GaitPart: Temporal Part-based Model for Gait Recognition》
  • 原文地址:https://blog.csdn.net/bigorsmallorlarge/article/details/137291723