<JavaEE><多线程>死锁

一、死锁概述

死锁指两个或两个以上线程互相占有对方所需锁资源,由于锁的不可抢占性,使这些线程都陷入停滞的状态。例如线程A的持有锁a,线程B持有锁b,在都未解锁的情况下线程A想要对b对象加锁,线程B想要对a对象进行加锁,由于锁除非加锁者主动解锁否则无法被抢占这一基本特性,线程A和线程B都进入等待状态,等候对方解锁,然而不继续执行双方都不会解锁,由此形成逻辑闭环。

public class ThreadDemo1 {
    static Object a = new Object();
    static Object b = new Object();
    public static void main(String[] args) {
        Thread A = new Thread(() -> {
            synchronized(a) {
                //等待确保B线程获取到锁b
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(b) {
                    System.out.println("t1拿到两把锁");
                }
            }
        });
        Thread B  = new Thread(() -> {
            synchronized(b) {
                //等待确保A线程获取到锁a
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(a) {
                    System.out.println("t2拿到两把锁");
                }
            }
        });
        A.start();
        B.start();
    }
}

运行结果如下,死锁产生,进程停止

二、死锁产生的原因

死锁产生需要具备四个条件,缺一不可

1.互斥使用

当一个线程拿到了这把锁,另一个线程也想获取,就只能阻塞等待

2.不可抢占

一个线程拿到锁之后只能主动解锁,不会让别的线程将锁抢占

3.请求保持

一个线程占有锁资源,同时又申请其他已被占有的资源,陷入阻塞

4.循环等待

等待线程间形成逻辑闭环,永久阻塞

三、避免死锁

1、加锁顺序:

当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。

public class ThreadDemo1 {
    static Object a = new Object();
    static Object b = new Object();
    public static void main(String[] args) {
        Thread A = new Thread(() -> {
            synchronized(a) {
                //调整加锁顺序,使两线程都首先获取到a锁才能获取b锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(b) {
                    System.out.println("t1拿到两把锁");
                }
            }
        });
        Thread B  = new Thread(() -> {
            synchronized(a) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(b) {
                    System.out.println("t2拿到两把锁");
                }
            }
        });
        A.start();
        B.start();
    }
}

2、加锁时限:

加上一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。但是如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。

3、死锁检测:

死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。
————————————————
(版权声明:本段来自CSDN博主「孔子-说」的原创文章)

待补充……

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值