Java发生死锁的条件及示例

1. 引言

在并发编程中,死锁是一种常见的问题。当多个线程彼此持有对方需要的资源时,它们可能会陷入一种无法继续执行的状态,这就是死锁。本文将介绍Java中发生死锁的条件,并通过示例代码来说明这些条件如何导致死锁。

2. 死锁的条件

发生死锁的必要条件有四个,也称为死锁的四个必要条件:

  • 互斥条件:资源一次只能被一个线程持有。
  • 请求与保持条件:一个线程至少要持有一个资源,并请求其他线程持有的资源。
  • 不可剥夺条件:资源只能在线程释放之后才能被其他线程获取。
  • 循环等待条件:若干线程之间形成一种循环等待资源的关系。

当这四个条件同时满足时,就可能发生死锁。下面通过示例代码来说明这些条件如何导致死锁。

3. 示例代码

3.1. 互斥条件

互斥条件意味着同一时间只能有一个线程持有某个资源。下面的示例代码展示了一个简单的死锁情况:

public class DeadlockExample {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: Holding resource 1");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("Thread 1: Holding resource 1 and resource 2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2: Holding resource 2");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("Thread 2: Holding resource 1 and resource 2");
                }
            }
        });

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

在上面的代码中,两个线程分别持有resource1resource2,并尝试获取对方持有的资源。由于互斥条件,当一个线程占用了resource1后,另一个线程无法获取到该资源,从而发生死锁。

3.2. 请求与保持条件

请求与保持条件指的是一个线程在持有某个资源的同时,又请求其他线程持有的资源。下面的示例代码展示了一个请求与保持条件导致死锁的情况:

public class DeadlockExample {
    private static class Resource {
        private Resource resource;

        public synchronized void setResource(Resource resource) {
            this.resource = resource;
        }

        public synchronized void useResource() {
            System.out.println("Using resource");
        }
    }

    public static void main(String[] args) {
        Resource resource1 = new Resource();
        Resource resource2 = new Resource();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: Holding resource 1");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                resource1.setResource(resource2);

                synchronized (resource2) {
                    resource1.useResource();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2: Holding resource 2");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                resource2.setResource(resource1);

                synchronized (resource1) {
                    resource2.useResource();
                }
            }
        });

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

在上面的代码中,线程1先持有resource1,然后请求resource2。同时,线程2先持有resource2,然后请求resource1。由于请求与保持条件,两个线程都在持有一个资源的同时请求另一个资源,从而发生死锁。