面试专题:简单写一个会导致死锁的程序

2023-12-27 11:04:38 浏览数 (1)

前言

首先简单介绍一下什么是死锁。死锁是指两个或者两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推荐下去;如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

面试题

这是一道真实的线程面试题目,要求的写一个会导致死锁的程序。

死锁程序思路的话,可以模拟两个线程,比如线程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腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!

0 人点赞