原创

java面试题-什么情况下导致线程死锁,遇到线程死锁该怎么解决?

1. 什么是线程死锁?

线程死锁是指多个线程因竞争资源而造成的一种僵局,互相等待对方释放资源,导致所有线程无法向前推进。死锁产生的必要条件包括互斥条件、不剥夺条件、请求和保持条件以及循环等待条件。

2. 死锁的必要条件

2.1 互斥条件

线程对资源的访问是排他性的,某一时刻只能有一个线程占有资源。

2.2 不剥夺条件

线程获得的资源在未使用完毕之前,不能被其他线程强行夺走,只能由获得资源的线程自己释放。

2.3 请求和保持条件

线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有。

2.4 循环等待条件

存在一个线程资源的循环等待链,每个线程已获得的资源同时被链中下一个线程所请求。

3. 线程死锁的案例代码

考虑以下简单的死锁案例代码:

Runnable r1 = () -> {
    synchronized ("A") {
        // 拿到A锁
        System.out.println(Thread.currentThread().getName() + "拿到A锁");
        synchronized ("B") {
            // 拿到A锁和B锁
            System.out.println(Thread.currentThread().getName() + "拿到A锁和B锁");
        }
    }
};

Runnable r2 = () -> {
    synchronized ("B") {
        // 拿到B锁
        System.out.println(Thread.currentThread().getName() + "拿到B锁");
        synchronized ("A") {
            // 拿到A锁和B锁
            System.out.println(Thread.currentThread().getName() + "拿到A锁和B锁");
        }
    }
};

Thread t1 = new Thread(r1, "线程1");
Thread t2 = new Thread(r2, "线程2");
t1.start();
t2.start();

上述代码中,两个线程分别试图获取锁"A"和锁"B",由于获取锁的顺序不同,可能会导致死锁的发生。

4. 如何避免死锁?

4.1 加锁顺序

通过规定线程获取锁的顺序,可以避免死锁。以下是一个示例代码:

Runnable r1 = () -> {
    synchronized ("A") {
        // 拿到A锁
        System.out.println(Thread.currentThread().getName() + "拿到A锁");
        try {
            // 等待并释放A的锁标记
            "A".wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized ("B") {
            // 拿到A锁和B锁
            System.out.println(Thread.currentThread().getName() + "拿到A锁和B锁");
        }
    }
};

Runnable r2 = () -> {
    synchronized ("B") {
        // 拿到B锁
        System.out.println(Thread.currentThread().getName() + "拿到B锁");
        synchronized ("A") {
            // 拿到A锁和B锁
            System.out.println(Thread.currentThread().getName() + "拿到A锁和B锁");
            // 释放A的线程锁
            "A".notify();
        }
    }
};

Thread t1 = new Thread(r1, "线程1");
Thread t2 = new Thread(r2, "线程2");
t1.start();
t2.start();

4.2 加锁时限

在线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁。这需要结合tryLock方法使用,超时时间可以由业务逻辑决定。

5. 总结

线程死锁是多线程编程中常见的问题,需要仔细设计程序结构来避免。通过规定加锁顺序和加锁时限等技术手段,可以有效预防和解决线程死锁问题。

正文到此结束
本文目录