Java面试题之多线程 用Java编程一个会导致死锁的程序,你将怎么解决?
在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
。由于两个线程互相等待对方释放锁,导致了死锁。
锁的顺序:确保所有线程以相同的顺序获取锁。例如,所有线程都应该先获取lock1
,然后获取lock2
。
使用定时锁:使用tryLock()
方法(来自java.util.concurrent.locks.Lock
接口)来尝试获取锁,如果获取失败,则可以选择放弃或重试。
避免嵌套锁:尽量减少锁的嵌套使用,避免在持有一个锁的情况下去请求另一个锁。
使用死锁检测:在某些情况下,可以使用死锁检测算法来检测和恢复死锁状态。
使用更高层次的并发工具:Java的java.util.concurrent
包提供了许多高层次的并发工具,如Semaphore
、CountDownLatch
、CyclicBarrier
等,可以帮助避免死锁。
以下是一个修改后的示例,使用锁的顺序来避免死锁:
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();
}
}
在这个修改后的示例中,两个线程都按照相同的顺序获取锁,从而避免了死锁的发生。