0
点赞
收藏
分享

微信扫一扫

【多线程 - 11、死锁】

f12b11374cba 2023-11-21 阅读 38

死锁

1、介绍

2、造成死锁的原因

互斥条件:
同一资源同时只能由一个线程读取

不可抢占条件:
不能强行剥夺线程占有的资源

请求和保持条件:
请求其他资源的同时对自己手中的资源保持不放

循环等待条件:
在相互等待资源的过程中,形成一个闭环

预防死锁:
只需要破坏其中一个条件即可,比如使用定时锁、尽量让线程用相同的加锁顺序,加锁超时或自动释放,死锁检测算法等等

3、避免死锁的方法

  • 固定加锁的顺序(针对锁顺序死锁)
  • 开放调用(针对对象之间协作造成的死锁)
  • 使用定时锁–>tryLock();如果等待获取锁时间超时,则抛出异常而不是一直等待

二、顺序死锁

例子

public class ThreadTest6 {
    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(demo,"1").start();
        new Thread(demo,"2").start();
        new Thread(demo,"3").start();
        new Thread(demo,"4").start();
    }

}

class Demo implements Runnable{
    Account a = new Account("A",1000);
    Account b = new Account("B",1000);

    @Override
    public void run() {
        transferMoney(a,b,100);
        transferMoney(b,a,100);
    }

    public void  transferMoney(Account fromAccount, Account toAccount,double money) {
        synchronized (fromAccount) {
            System.out.println("线程" + Thread.currentThread().getName() + "得到锁" + fromAccount.getName());
            synchronized (toAccount) {
                System.out.println("线程" + Thread.currentThread().getName() + "得到锁" + toAccount.getName());
                if(fromAccount.getMoney() < money) {
                    System.out.println("余额不足");
                } else {
                    fromAccount.setMoney(fromAccount.getMoney()-money);
                    toAccount.setMoney(toAccount.getMoney() + money);
                    System.out.println("转账后:" + fromAccount.getName() + "有:" + fromAccount.getMoney());
                    System.out.println("转账后:" + toAccount.getName() + "有:" + toAccount.getMoney());
                }
            }
        }
    }
}
class Account{

    public Account(String name, double money) {
        this.name = name;
        this.money = money;
    }
    
    private String name;
    private double money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

}

避免死锁

上面的例子改造:

public class InduceLockOrder {

    // 额外的锁、避免两个对象hash值相等的情况(即使很少)
    private static final Object tieLock = new Object();

    public void transferMoney(final Account fromAcct,  final Account toAcct, final DollarAmount amount)  throws InsufficientFundsException {
        class Helper {
            public void transfer()  throws InsufficientFundsException {
                if (fromAcct.getBalance().compareTo(amount) < 0)
                    throw new InsufficientFundsException();
                else {
                    fromAcct.debit(amount);
                    toAcct.credit(amount);
                }
            }
        }
        // 得到锁的hash值
        int fromHash = System.identityHashCode(fromAcct);
        int toHash = System.identityHashCode(toAcct);

        // 根据hash值来上锁
        if (fromHash < toHash) {
            synchronized (fromAcct) {
                synchronized (toAcct) {
                    new Helper().transfer();
                }
            }

        } else if (fromHash > toHash) {// 根据hash值来上锁
            synchronized (toAcct) {
                synchronized (fromAcct) {
                    new Helper().transfer();
                }
            }
        } else {// 额外的锁、避免两个对象hash值相等的情况(即使很少)
            synchronized (tieLock) {
                synchronized (fromAcct) {
                    synchronized (toAcct) {
                        new Helper().transfer();
                    }
                }
            }
        }
    }
}

三、协作对象之间发生死锁

发生条件

开放调用避免死锁

四、定时锁避免死锁

tryLock 方法

例子

public class tryLock {
    public static void main(String[] args) {
        System.out.println("开始");
        final Lock lock = new ReentrantLock();
        new Thread() {
            @Override
            public void run() {
                String tName = Thread.currentThread().getName();
                if (lock.tryLock()) {
                    System.out.println(tName + "获取到锁!");
                } else {
                    System.out.println(tName + "获取不到锁!");
                    return;
                }
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }
                    Thread.sleep(5000);
                } catch (Exception e) {
                    System.out.println(tName + "出错了!!!");
                } finally {
                    System.out.println(tName + "释放锁!!");
                    lock.unlock();
                }

            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                String tName = Thread.currentThread().getName();

                if (lock.tryLock()) {
                    System.out.println(tName + "获取到锁!");
                } else {
                    System.out.println(tName + "获取不到锁!");
                    return;
                }

                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }

                } catch (Exception e) {
                    System.out.println(tName + "出错了!!!");
                } finally {
                    System.out.println(tName + "释放锁!!");
                    lock.unlock();
                }
            }
        }.start();
        System.out.println("结束");
    }
}

五、总结

发生死锁的原因主要由于:

举报

相关推荐

0 条评论