用代码角度说死锁的解决方案

1 死锁问题

1.1 什么是死锁

线程A,占有资源A,并且等待占有资源B。
线程B,占有资源B,并且等待占有资源A。

1.2 造成死锁的原因

  1. 互斥
    • 共享的资源,只能够被一个线程占用。
    • 共享资源只能一对一
  2. 占有且等待
    • 线程A,占有资源A,等待资源B时,不会释放资源A。
    • 占着茅坑不拉屎。
  3. 不可抢占
    • 其他线程,不能够抢占别的线程的资源。
    • 互相谦让,看上去和谐,实际上却解决不了问题。
  4. 循环等待
    • 线程们一旦进入等待状态,就会一直等下去。
    • 死脑筋,不懂得变通。

1.3 从代码说是如何避免死锁的

一旦发生死锁,没什么好办法,只能重启。

所以应该从死锁产生的原因入手,解决死锁产生的原因,避免死锁。

1.3.1 互斥

无法改变。这是共享资源定义的客观事实。

1.3.2 破坏占有且等待

如果可以一次性申请所有的资源,就可以避免等待。

以两个账户AB转账为例子。

    public class DeadLock{
        public static void main(String[] args){
            Account a = new Accout();
            Account b = new Accout();
            // a向b转100,b向a转100
            a.transfer(b, 100);
            b.transfer(a, 100);
        }
    }
        // 一次性申请所有的资源
    static class Allocator{
        private List<Object> als = new ArrayList<Object>;

        synchronized boolean apply(Object from, Object to){
            if(als.contains(from) || als.contains(to)){
                return false;
            } else{
                als.add(from);
                als.add(to);
            }
            return true;
        }

        synchronized void clean(Object from, Object to){
            als.remove(from);
            als.remove(to);
        }
    }

    static class Accout{
        // 单例设计模式
        private Allocator actr = Allocator.getInstance();
        private int balance;
        void transfer(Account target, int amt){
            while(!actr.apply(this, target));
            try{
                // using synchronized  一口气获得所有的资源。
                synchronized(this){
                    sout(this.toString() + " lock obj1");
                    synchronized(target){
                        sout(this.toString() + " lock obj2");
                        if(this.balance > amt){
                            this.balance -= amt;
                            target.balance += amt;
                        }
                    }
                }
            } finally{
                actr.clean(this, target);
            }
        }
    }
    
    private void Allocator(){};

    private static class singleTonHoler{
        private static Allocator INSTANCE = new Allocator();
    }

    public static Allocator getInstanece(){
        return SingleTonHoler.INSTANCE;
    }

1.3.3 破坏不可抢占条件

换句话说,当一个线程发现自己要死锁了,就应该把自己占有的资源释放出去。

synchronized做不到。当synchronized申请不到资源时,线程就直接进入阻塞状态,自然无法释放已经占有资源。

JDK中的java.util.concurrent提供了Lock来解决这个问题。

显式使用Lock类中的定时tryLock功能来替代内置锁机制,可以检测死锁,并且从死锁状态中恢复过来。

使用内置锁的线程获取不到锁会被阻塞,而显式锁可以指定一个个超时时间(TimeOut),在等待超过这个时间后,tryLock就会返回一个失败信息,并且释放其拥有的资源。

    public class DeadLock{
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();

        public static void main(String[] args){
            Thread a = new Thread(new Lock1());
            Thread b = new Thread(new Lock2());
            a.start();
            b.start();
        }

        static class Lock1 implements Runnable{
            @Override
            public void run(){
                try{
                    sout("lock1 running");
                    while(true){
                        if(lock1.tryLock(1, TimeUnit.MILLISECONDS)){
                            sout("Lock1 lock obj1");
                            if(lock2.tryLock(1, TimeUnit.MILLISECONDS)){
                                sout("Lock1 lock obj2");
                            }
                        }
                    }
                } catch (Exception e){
                    e.printStackTrace;
                } finally {
                    lock1.unlock();
                    lock2.unlock();
                }
            }
        }
    }

1.3.4 破坏循环等待条件

思路是:对系统中的资源进行统一编号,进程可以在任何时刻提出资源申请,必须按照资源的编号顺序提出。这样做就能保证系统不出现死锁。这就是资源有序分配法。

    class Account{
        private int id;
        private int balance;
        void transfer(Account target, int amt){
            Account left = this;
            Account right = target;
            if(this.id > target.id){
                left = target;
                right = this;
            }
            synchronized(left){
                synchronized(right){
                    if(this.balance > amt){
                        this.balance -= amt;
                        target.balance += amt;
                    }
                }
            }
        }
    }
posted @ 2021-06-11 16:48  Yiyang_Cai  阅读(209)  评论(0编辑  收藏  举报