前言
首先简单介绍一下什么是死锁。死锁是指两个或者两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推荐下去;如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。
面试题
这是一道真实的线程面试题目,要求的写一个会导致死锁的程序。
死锁程序思路的话,可以模拟两个线程,比如线程AAA先拿lockA锁,其他线程就拿不到lockA,只有AAA完成之后才释放,但是线程AAA完成还得lockB锁,可以在初始化另一个线程BBB,此时lockB锁在BBB线程拿着。BBB先拿lockB锁,其他线程也拿不到lockB。这时候线程AAA和线程BBB就会互斥,具体看代码。
代码语言:java复制class HoldLockThread implements Runnable{
private String lockA;
private String lockB;
public HoldLockThread(String lockA,String lockB){
this.lockA = lockA;
this.lockB = lockB;
}
/**
* 完成这个线程需要获得2个锁
*/
@Override
public void run() {
//拿着lockA
synchronized (lockA){
// 还要获取 lockB
synchronized (lockB){
System.out.println("正在获取第二个锁:" Thread.currentThread().getName() "t 自己持有" lockB "t 尝试获得:" lockA);
}
}
}
}
/**
* 死锁demo
*/
public class DeamLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldLockThread(lockA,lockB),"ThreadAAA").start();
new Thread(new HoldLockThread(lockB,lockA),"ThreadBBB").start();
}
}
查看运行结果,如图所示,可以看到程序一直在运行中,不终止,并且AAA已获得了lockA,正在等待获取lockB,而BBB已获得了lockB,正在等待获取lockA,很显然,两个线程存在互斥,都在等待不可能获得锁(资源),这就会导致整个程序死锁。
上面结果,或许不能直接看出程序是死锁,所以可以通过java命令,直接查看程序线程运行情况。
查看java线程情况,查看当前程序线程运行情况
代码语言:shell复制jps
代码语言:shell复制jstack 线程号
可以看到ThreadAAA和ThreadBBB都是在运行中,AAA已获得了lockA,正在等待获取lockB,而BBB已获得了lockB,并且在日志最后可以看到Found 1 deadlock,这说明这是一个死锁程序了。
以上就是该面试题的答案了,只要创建两个线程,先各种获得一个锁,然后再各种抢占对方的锁。
如何避免死锁
这其实是另一个问题了,程序开发不可避免会出现死锁的情况,但是在开发中我们要尽量避免。
- 避免一个线程同时获取多个锁。
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
- 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!