MST
星途 面试题库

面试题:Java多线程编程中如何避免死锁

在Java多线程编程场景下,详细阐述产生死锁的原因,并说明至少两种避免死锁的方法,可结合代码示例说明。
29.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

死锁产生的原因

  1. 资源竞争:多个线程同时竞争有限的资源,例如锁、文件句柄等。当线程获取资源的顺序不一致时,就可能导致死锁。
  2. 循环等待:线程之间形成一种环形的等待关系,每个线程都在等待下一个线程释放其所需要的资源,从而形成死锁。

避免死锁的方法

  1. 破坏死锁的四个必要条件
    • 互斥条件:资源不能同时被多个线程访问。这个条件在大多数场景下是无法破坏的,因为资源本身就需要保证其独占性。
    • 占有并等待条件:可以让线程一次性获取所有需要的资源,而不是先获取部分资源,再等待其他资源。例如:
public class DeadlockAvoidance1 {
    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 acquired both resources");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                synchronized (resource2) {
                    System.out.println("Thread 2 acquired both resources");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
- **不可剥夺条件**:当一个线程获取到资源后,不能被其他线程强制剥夺。在一些场景下,可以通过设置超时来破坏这个条件。例如:
import java.util.concurrent.TimeUnit;

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

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            boolean success1 = false;
            boolean success2 = false;
            try {
                success1 = resource1.wait(1000);
                success2 = resource2.wait(1000);
                if (success1 && success2) {
                    System.out.println("Thread 1 acquired both resources");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (success1) {
                    synchronized (resource1) {
                        resource1.notify();
                    }
                }
                if (success2) {
                    synchronized (resource2) {
                        resource2.notify();
                    }
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            boolean success1 = false;
            boolean success2 = false;
            try {
                success1 = resource1.wait(1000);
                success2 = resource2.wait(1000);
                if (success1 && success2) {
                    System.out.println("Thread 2 acquired both resources");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (success1) {
                    synchronized (resource1) {
                        resource1.notify();
                    }
                }
                if (success2) {
                    synchronized (resource2) {
                        resource2.notify();
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
- **循环等待条件**:对资源进行排序,所有线程按照相同的顺序获取资源。例如:
public class DeadlockAvoidance3 {
    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 acquired both resources");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                synchronized (resource2) {
                    System.out.println("Thread 2 acquired both resources");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
  1. 使用java.util.concurrent.locks.ReentrantLock并设置超时ReentrantLock提供了比synchronized更灵活的锁控制。例如:
import java.util.concurrent.locks.ReentrantLock;

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

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            boolean success1 = false;
            boolean success2 = false;
            try {
                success1 = lock1.tryLock(1, TimeUnit.SECONDS);
                success2 = lock2.tryLock(1, TimeUnit.SECONDS);
                if (success1 && success2) {
                    System.out.println("Thread 1 acquired both resources");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (success1) {
                    lock1.unlock();
                }
                if (success2) {
                    lock2.unlock();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            boolean success1 = false;
            boolean success2 = false;
            try {
                success1 = lock1.tryLock(1, TimeUnit.SECONDS);
                success2 = lock2.tryLock(1, TimeUnit.SECONDS);
                if (success1 && success2) {
                    System.out.println("Thread 2 acquired both resources");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (success1) {
                    lock1.unlock();
                }
                if (success2) {
                    lock2.unlock();
                }
            }
        });

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