Java多线程死锁示例及解决方案

Answered using GPT-4o-mini by Java Q&A Hub

Question:

Java面试题之多线程 用Java编程一个会导致死锁的程序,你将怎么解决?

Answer:

在Java中,死锁是指两个或多个线程在执行过程中,因为争夺资源而造成的一种互相等待的现象。为了演示死锁的情况,我们可以编写一个简单的程序,其中两个线程试图获取两个锁,但以相反的顺序进行,从而导致死锁。

以下是一个会导致死锁的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) {}
                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) {}
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock 1!");
                }
            }
        });

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

在这个示例中,thread1首先获取lock1,然后尝试获取lock2;而thread2则首先获取lock2,然后尝试获取lock1。由于两个线程互相等待对方释放锁,导致了死锁。

解决死锁的方法

  1. 锁的顺序:确保所有线程以相同的顺序获取锁。例如,所有线程都应该先获取lock1,然后获取lock2

  2. 使用定时锁:使用tryLock()方法(来自java.util.concurrent.locks.Lock接口)来尝试获取锁,如果获取失败,则可以选择放弃或重试。

  3. 避免嵌套锁:尽量减少锁的嵌套使用,避免在持有一个锁的情况下去请求另一个锁。

  4. 使用死锁检测:在某些情况下,可以使用死锁检测算法来检测和恢复死锁状态。

  5. 使用更高层次的并发工具:Java的java.util.concurrent包提供了许多高层次的并发工具,如SemaphoreCountDownLatchCyclicBarrier等,可以帮助避免死锁。

以下是一个修改后的示例,使用锁的顺序来避免死锁:

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

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

在这个修改后的示例中,两个线程都按照相同的顺序获取锁,从而避免了死锁的发生。