面试题答案
一键面试死锁产生的原因
死锁通常发生在多个线程相互等待对方释放锁的情况下。例如,假设有两个线程 Thread1
和 Thread2
,以及两把锁 lock1
和 lock2
。Thread1
获得了 lock1
,然后尝试获取 lock2
,而此时 Thread2
获得了 lock2
,又尝试获取 lock1
,这样两个线程就会相互等待,造成死锁。
示例代码(死锁场景)
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockExample {
private static final ReentrantLock lock1 = new ReentrantLock();
private static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
lock1.lock();
try {
System.out.println("Thread1 acquired lock1");
Thread.sleep(100);
lock2.lock();
try {
System.out.println("Thread1 acquired lock2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
});
Thread thread2 = new Thread(() -> {
lock2.lock();
try {
System.out.println("Thread2 acquired lock2");
Thread.sleep(100);
lock1.lock();
try {
System.out.println("Thread2 acquired lock1");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
});
thread1.start();
thread2.start();
}
}
预防死锁的策略
- 按照固定顺序获取锁:所有线程按照相同的顺序获取锁,这样可以避免相互等待。
- 使用定时锁:尝试获取锁时设置一个超时时间,如果在超时时间内没有获取到锁,则放弃获取并进行其他操作。
示例代码(预防死锁 - 固定顺序获取锁)
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockPreventionExample {
private static final ReentrantLock lock1 = new ReentrantLock();
private static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
// 始终先获取 lock1,再获取 lock2
lock1.lock();
try {
System.out.println("Thread1 acquired lock1");
Thread.sleep(100);
lock2.lock();
try {
System.out.println("Thread1 acquired lock2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
});
Thread thread2 = new Thread(() -> {
// 同样先获取 lock1,再获取 lock2
lock1.lock();
try {
System.out.println("Thread2 acquired lock1");
Thread.sleep(100);
lock2.lock();
try {
System.out.println("Thread2 acquired lock2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
});
thread1.start();
thread2.start();
}
}
示例代码(预防死锁 - 使用定时锁)
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockPreventionWithTimeoutExample {
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 gotLock1 = false;
boolean gotLock2 = false;
try {
gotLock1 = lock1.tryLock(100, TimeUnit.MILLISECONDS);
if (gotLock1) {
System.out.println("Thread1 acquired lock1");
gotLock2 = lock2.tryLock(100, TimeUnit.MILLISECONDS);
if (gotLock2) {
System.out.println("Thread1 acquired lock2");
} else {
System.out.println("Thread1 could not acquire lock2, releasing lock1");
}
} else {
System.out.println("Thread1 could not acquire lock1");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (gotLock2) {
lock2.unlock();
}
if (gotLock1) {
lock1.unlock();
}
}
});
Thread thread2 = new Thread(() -> {
boolean gotLock1 = false;
boolean gotLock2 = false;
try {
gotLock2 = lock2.tryLock(100, TimeUnit.MILLISECONDS);
if (gotLock2) {
System.out.println("Thread2 acquired lock2");
gotLock1 = lock1.tryLock(100, TimeUnit.MILLISECONDS);
if (gotLock1) {
System.out.println("Thread2 acquired lock1");
} else {
System.out.println("Thread2 could not acquire lock1, releasing lock2");
}
} else {
System.out.println("Thread2 could not acquire lock2");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (gotLock1) {
lock1.unlock();
}
if (gotLock2) {
lock2.unlock();
}
}
});
thread1.start();
thread2.start();
}
}
通过以上策略,可以有效地避免在使用可重入锁时多线程场景下的死锁问题。