线程死锁的定义

线程死锁是多线程编程里的一种问题,指的是两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的状态,在无外力作用时,这些线程都将无法继续执行下去。

例如,线程 A 持有资源 X 并请求资源 Y,而线程 B 持有资源 Y 并请求资源 X,这样两个线程就会陷入互相等待的状态,进而产生死锁。以下是一个简单的死锁示例代码:

public class DeadlockExample {
    private static final Object resource1 = new Object();
    private static final 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(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for resource 2...");
                synchronized (resource2) {
                    System.out.println("Thread 1: Holding resource 1 and 2...");
                }
            }
        });

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

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

在上述代码中,thread1 持有 resource1 并尝试获取 resource2,而 thread2 持有 resource2 并尝试获取 resource1,这样就可能导致死锁。

避免线程死锁的方法

1. 破坏互斥条件

互斥条件指的是资源在同一时间只能被一个线程使用。不过大部分资源本身就具有互斥性,所以通常很难破坏这一条件。

2. 破坏占有并等待条件

  • 一次性获取所有资源:线程在开始执行前,一次性请求它所需要的全部资源,若无法获取所有资源,就等待,直到所有资源都可用。
public class AvoidDeadlockExample {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();

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

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

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

3. 破坏不剥夺条件

当一个线程已经持有了某些资源,在它请求其他资源而无法立即获取时,它必须释放已持有的资源,等以后需要时再重新获取。例如使用 ReentrantLocktryLock() 方法。

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

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

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (true) {
                if (lock1.tryLock()) {
                    try {
                        if (lock2.tryLock()) {
                            try {
                                System.out.println("Thread 1: Holding resource 1 and 2...");
                            } finally {
                                lock2.unlock();
                            }
                        }
                    } finally {
                        lock1.unlock();
                    }
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                if (lock2.tryLock()) {
                    try {
                        if (lock1.tryLock()) {
                            try {
                                System.out.println("Thread 2: Holding resource 1 and 2...");
                            } finally {
                                lock1.unlock();
                            }
                        }
                    } finally {
                        lock2.unlock();
                    }
                }
            }
        });

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

4. 破坏循环等待条件

对资源进行排序,线程必须按照固定的顺序请求资源。这样就不会出现循环等待的情况。

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

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

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

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

通过上述这些方法,可以有效避免线程死锁的发生。