编写一段代码,使得这段代码必定会产生死锁

使用Thread.sleep

以下是一个经典的 Java 死锁实现,通过两个线程互相持有对方需要的锁来确保必定发生死锁:

public class DeadlockDemo {
    // 创建两个锁对象
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    
    public static void main(String[] args) {
        // 线程1:先获取lock1,再尝试获取lock2
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("线程1持有lock1,等待lock2...");
                try {
                    Thread.sleep(10000); // 等个10秒增加死锁概率
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("线程1成功获取lock2");
                }
            }
        });
        // 线程2:先获取lock2,再尝试获取lock1
        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程2持有lock2,等待lock1...");
                try {
                    Thread.sleep(10000); // 等个10秒增加死锁概率
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("线程2成功获取lock1");
                }
            }
        });
        
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("程序正常结束(这行永远不会执行)");
    }
}

不使用Thread.sleep

可以使用 countDownLatch 来实现死锁,思路为:

  1. 新建一个count为2的 CountDownLatch 对象 latch。
  2. thread1 持有 lock1 后,调用 latch.countDown() 将计数减一,随后调用 latch.await() 等待,直到 thread2 也持有 lock2 后调用 latch.countDownd()
  3. thread2 持有 lock2 后,调用 latch.countDown()将计数减一,随后调用 latch.await() 直到 thread1 调用 latch.countDown()
  4. thread1 想要 lock2,但 thread2 持有了它
  5. thread2 想要 lock1,但 thread1 持有了它
  6. 由于互相等待对方释放锁,因此死锁发生
import java.util.concurrent.CountDownLatch;

public class GuaranteedDeadlock {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    private static final CountDownLatch latch = new CountDownLatch(2);

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock1...");
                latch.countDown(); // 让 thread2 也开始执行
                
                try { 
                    latch.await(); // 保证两个线程同时竞争
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } 
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock2!");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock2...");
                latch.countDown(); // 让 thread1 也开始执行
                
                try { 
                    latch.await(); // 保证两个线程同时竞争
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } 
                
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock1!");
                }
            }
        });

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

除此之外,也可以使用CycliBarrier

错误示范

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 lock1...");
                
                // 试图获取 lock2
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock2!");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock2...");
                
                // 试图获取 lock1(但 lock1 此时被 Thread1 持有)
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock1!");
                }
            }
        });

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

上述的代码无法保证死锁,因为Java 的线程调度是由操作系统决定的,线程的执行顺序是不可预测的,thread1 可能会在 thread2 运行前快速获取 lock1 和lock2,然后释放它们,从而避免死锁

所以这段代码死锁的发生概率不是 100%。这也是为什么第一种方式要使用Thread.sleep来保证两个线程都分别获得了锁


程序员Seven
53 声望6 粉丝

程序员Seven,记录Seven的菜鸟成长之路。在线阅读网站:www.seven97.top