死锁是因为多个线程互相等待对方释放资源,导致谁也动不了。🛑

要避免死锁,可以破坏“互斥、占有且等待、不可抢占、循环等待”这四个条件中的一个。🔑

例如:

让资源共享 ✅
一次性申请所有资源 ✅
允许资源被抢占 ✅
避免循环等待 ✅
知识内容:
🔍 死锁的四个必要条件:

互斥条件:每个资源只能被一个线程占用。
占有和等待:线程在持有至少一个资源的同时,等待获取其他资源。
不可抢占:线程所获得的资源在未使用完毕之前不能被其他线程抢占。
循环等待:多个线程形成一种头尾相接的循环等待资源关系。
🔍 避免死锁的方法:

按序申请资源:确保所有线程在获取多个锁时,按照相同的顺序获取锁。
尽量减少锁的范围:将锁的粒度尽可能缩小,减少持有锁的时间。可以通过拆分锁或使用更细粒度的锁来实现。
使用尝试锁机制:使用 ReentrantLock 的 tryLock 方法,尝试在一段时间内获取锁,如果无法获取,则可以选择放弃或采取其他措施,避免死锁。
设置超时等待时间:为锁操作设置超时,防止线程无限期地等待锁。
避免嵌套锁:尽量避免在一个锁的代码块中再次尝试获取另一个锁。
🎉 代码演示:

public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        synchronized (lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            synchronized (lock2) {
                System.out.println("Thread 1: Holding lock 1 and lock 2...");
            }
        }
    });

    Thread thread2 = new Thread(() -> {
        synchronized (lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            synchronized (lock1) {
                System.out.println("Thread 2: Holding lock 2 and lock 1...");
            }
        }
    });

    thread1.start();
    thread2.start();
}

}

🔑 通俗解释:

想象一下,两个小朋友 A 和 B 在抢玩具。A 拿着玩具车等 B 的积木,B 拿着积木等 A 的玩具车,结果谁都玩不成!这就是死锁。为了避免这种情况,我们可以让 A 和 B 按照相同的顺序拿玩具,或者让他们不要同时抢两个玩具。😅
避免死锁的代码示例

🔍 按序申请资源:

public class AvoidDeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        synchronized (lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            synchronized (lock2) {
                System.out.println("Thread 1: Holding lock 1 and lock 2...");
            }
        }
    });

    Thread thread2 = new Thread(() -> {
        synchronized (lock1) {
            System.out.println("Thread 2: Holding lock 1...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 1 and lock 2...");
            }
        }
    });

    thread1.start();
    thread2.start();
}

}

🔍 使用尝试锁机制:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockExample {
private static final Lock lock1 = new ReentrantLock();
private static final Lock lock2 = new ReentrantLock();

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        try {
            if (lock1.tryLock()) {
                System.out.println("Thread 1: Holding lock 1...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                if (lock2.tryLock()) {
                    System.out.println("Thread 1: Holding lock 1 and lock 2...");
                    lock2.unlock();
                }
                lock1.unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    Thread thread2 = new Thread(() -> {
        try {
            if (lock2.tryLock()) {
                System.out.println("Thread 2: Holding lock 2...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                if (lock1.tryLock()) {
                    System.out.println("Thread 2: Holding lock 2 and lock 1...");
                    lock1.unlock();
                }
                lock2.unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    thread1.start();
    thread2.start();
}

}

🔑 通俗解释:

为了避免死锁,我们可以让线程按照相同的顺序获取锁,或者使用 tryLock 尝试获取锁。如果获取不到锁,线程可以选择放弃或等待一段时间再尝试,而不是一直等待。这样就能有效避免死锁的发生。😊

总结
🔒 死锁的发生需要满足四个必要条件:互斥、占有和等待、不可抢占、循环等待。
通过按序申请资源、减少锁的范围、使用尝试锁机制等方法,可以有效避免死锁。
记住,死锁就像小朋友抢玩具,只要大家按规矩来,就不会出问题!😅