什么是线程死锁?

线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

为了更好地理解线程死锁,我们可以来看一个实际的例子。这个例子模拟了两个线程互相等待对方持有的资源,从而进入死锁的情况。代码如下:

java

public class DeadLockDemo {
    private static Object resource1 = new Object(); // 资源1
    private static Object resource2 = new Object(); // 资源2

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + " get resource1");
                try {
                    Thread.sleep(1000); // 休眠1秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + " waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + " get resource2");
                }
            }
        }, "线程1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + " get resource2");
                try {
                    Thread.sleep(1000); // 休眠1秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + " waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + " get resource1");
                }
            }
        }, "线程2").start();
    }
}

输出结果

txt

Thread[线程1,5,main] get resource1
Thread[线程2,5,main] get resource2
Thread[线程1,5,main] waiting get resource2
Thread[线程2,5,main] waiting get resource1

分析

在上述代码中,线程1 先获取了 resource1 的监视器锁,然后休眠1秒钟。在这1秒钟的时间内,线程2 有机会获取 resource2 的监视器锁。接着,线程1 和 线程2 分别尝试获取对方持有的资源,从而形成了互相等待的局面,这就产生了死锁。

死锁的四个必要条件

上面的例子符合产生死锁的四个必要条件:

  1. 互斥条件:该资源任意一个时刻只由一个线程占用。
  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

如何避免死锁

为了避免死锁,可以采取以下几种策略:

1. 避免嵌套锁

尽量避免在一个锁内请求另一个锁,从而杜绝循环等待的可能性。

java

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

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + " get resource1");
                // 资源1的操作
            }
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + " get resource2");
                // 资源2的操作
            }
        }, "线程1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + " get resource2");
                // 资源2的操作
            }
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + " get resource1");
                // 资源1的操作
            }
        }, "线程2").start();
    }
}

2. 锁的顺序

确保所有线程按照相同的顺序请求锁,可以有效避免循环等待。

java

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

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + " get resource1");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + " get resource2");
                }
            }
        }, "线程1").start();

        new Thread(() -> {
            synchronized (resource1) { // 按照相同的顺序请求锁
                System.out.println(Thread.currentThread() + " get resource1");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + " get resource2");
                }
            }
        }, "线程2").start();
    }
}

3. 超时机制

使用超时机制来请求锁,如果超时未获得锁,则放弃请求并处理相应逻辑。

java

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

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + " get lock1");
                    Thread.sleep(500); // Simulate some work
                    if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            System.out.println(Thread.currentThread() + " get lock2");
                        } finally {
                            lock2.unlock();
                        }
                    } else {
                        System.out.println(Thread.currentThread() + " failed to get lock2");
                    }
                } else {
                    System.out.println(Thread.currentThread() + " failed to get lock1");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) {
                    lock1.unlock();
                }
            }
        }, "线程1").start();

        new Thread(() -> {
            try {
                if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + " get lock2");
                    Thread.sleep(500); // Simulate some work
                    if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            System.out.println(Thread.currentThread() + " get lock1");
                        } finally {
                            lock1.unlock();
                        }
                    } else {
                        System.out.println(Thread.currentThread() + " failed to get lock1");
                    }
                } else {
                    System.out.println(Thread.currentThread() + " failed to get lock2");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
            }
        }, "线程2").start();
    }
}

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值