Java 实例 - 死锁及解决方法

死锁(Deadlock)是多线程编程中的一种常见问题,指的是两个或多个线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,导致这些线程都无法继续执行下去。死锁通常发生在多个线程需要同时获取多个锁的情况下。


死锁产生的四个必要条件:

  1. 互斥条件:资源一次只能被一个线程占用。
  2. 占有并等待:线程持有至少一个资源,并等待获取其他被占用的资源。
  3. 非抢占条件:线程已持有的资源不能被其他线程强行抢占,只能由线程自己释放。
  4. 循环等待条件:存在一个线程等待的循环链,每个线程都在等待下一个线程所持有的资源。

Java 死锁示例:

以下是一个简单的死锁示例,两个线程互相等待对方释放锁:

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) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock 2!");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 2...");
                try {
                    Thread.sleep(100); // 模拟操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock 1!");
                }
            }
        });

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

运行结果:

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

此时,程序会卡住,因为 thread1 持有 lock1 并等待 lock2,而 thread2 持有 lock2 并等待 lock1,形成了死锁。


死锁的解决方法:

1. 避免嵌套锁:
  • 尽量减少多个锁的嵌套使用,避免同时持有多个锁。
2. 锁的顺序一致:
  • 确保所有线程以相同的顺序获取锁。例如,在上面的例子中,可以让两个线程都先获取 lock1,再获取 lock2

改进后的代码:

public class DeadlockSolution {
    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) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock 2!");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock1) { // 先获取 lock1
                System.out.println("Thread 2: Holding lock 1...");
                try {
                    Thread.sleep(100); // 模拟操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 2: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 2: Acquired lock 2!");
                }
            }
        });

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

运行结果:

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Acquired lock 2!
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Acquired lock 2!
3. 使用超时机制:
  • 使用 tryLock() 方法尝试获取锁,并设置超时时间。如果超时仍未获取到锁,则释放已持有的锁并重试。

示例:

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

public class DeadlockTimeoutSolution {
    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 {
                while (true) {
                    if (lock1.tryLock()) {
                        System.out.println("Thread 1: Holding lock 1...");
                        try {
                            if (lock2.tryLock()) {
                                System.out.println("Thread 1: Acquired lock 2!");
                                break;
                            }
                        } finally {
                            if (lock2.isHeldByCurrentThread()) lock2.unlock();
                        }
                    }
                    lock1.unlock();
                    Thread.sleep(100); // 重试前等待
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) lock1.unlock();
                if (lock2.isHeldByCurrentThread()) lock2.unlock();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                while (true) {
                    if (lock2.tryLock()) {
                        System.out.println("Thread 2: Holding lock 2...");
                        try {
                            if (lock1.tryLock()) {
                                System.out.println("Thread 2: Acquired lock 1!");
                                break;
                            }
                        } finally {
                            if (lock1.isHeldByCurrentThread()) lock1.unlock();
                        }
                    }
                    lock2.unlock();
                    Thread.sleep(100); // 重试前等待
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) lock1.unlock();
                if (lock2.isHeldByCurrentThread()) lock2.unlock();
            }
        });

        thread1.start();
        thread2.start();
    }
}
4. 使用工具检测死锁:
  • 使用工具(如 jstackVisualVM)检测死锁并分析线程状态。

总结:

死锁是多线程编程中的常见问题,可以通过以下方法避免或解决:

  1. 避免嵌套锁。
  2. 确保锁的顺序一致。
  3. 使用超时机制。
  4. 使用工具检测死锁。

你可能感兴趣的:(java,开发语言)