并发编程 - 死锁如何处理

一、死锁是个啥?

关于银行转账问题,a转给b,c转给d,涉及到两个用户,那么是否可以拿两把锁来操作呢?如下:

package exercise_01_0705_synchronized;

public class Account2 {
    private Integer balance = 10000; // 账户余额

    // 转账操作
    void transfer(Account2 account2,int amt) throws InterruptedException {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " this lock");
            Thread.sleep(1000);
            synchronized (account2) {
                System.out.println(Thread.currentThread().getName() + " account2 lock");
                account2.balance += amt;
                Thread.sleep(1000);
                this.balance -= amt;
                System.out.println(Thread.currentThread().getName() + " account2 unlock");
            }
            System.out.println(Thread.currentThread().getName() + " this unlock");
        }
    }

    public static void main(String[] args) {
        Account2 account = new Account2();
        Account2 account2 = new Account2();
        Account2 account3 = new Account2();
        Thread a = new Thread(()->{
            try {
                account.transfer(account2,100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread b = new Thread(()->{
            try {
                account2.transfer(account,90);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        a.start();
        b.start();
    }
}

这种骚操作就可能引起死锁哦。 如下:
在这里插入图片描述
在这里插入图片描述
转账发生死锁时候的资源分配图
在这里插入图片描述

二、死锁产生的四个必要条件

1、互斥,资源x和资源y都只能被一个线程占用;(锁的基本条件咯)
2、占有且等待,线程t1已经获得共享资源x,再去获得资源y的时候,不释放共享资源x;
3、不可抢占,其他线程不可抢占线程t1的资源;
4、循环等待,线程t1等线程t2的资源,线程t2等待线程t1的资源。

三、如何避免死锁?

破坏如上其中任一一个条件即可。

3.1 互斥(不可行)

这个不可以被破坏,锁的基本条件

3.2 破坏占有且等待条件

可以一次性申请所有所需资源。
如之前的转账问题,可以建立一个管理员来处理,逻辑如下:
在这里插入图片描述
代码:
单例模式的管理员:

package exercise_01_0705_synchronized;

import java.util.ArrayList;
import java.util.List;

// 单例模式
public class Allocator {
    private volatile static Allocator allocator = null; //volatitle 解决指令重排序的问题
    private static List<Object> lockList = new ArrayList<>();

    private Allocator() {
    }        // 构造方法私有

    public static Allocator getAllocator() { //synchronized  保证线程安全
        if (allocator == null) {
            // 保证所有线程都可以进入,不会因为锁方法而把类锁上,也可以过滤很多,提高性能
            synchronized (Allocator.class) { //开始锁住
                if (allocator == null) { //防止两个线程同城到达if,又出现线程不安全的问题,双重检查锁
                    allocator = new Allocator();
                }
            }
        }
        return allocator;
    }

    // 拿到资源
    public synchronized boolean getLock(Object a, Object b) {
        if (lockList.contains(a) || lockList.contains(b)) {
            System.out.println("false");
            return false;
        }
        lockList.add(a);
        lockList.add(b);
        System.out.println("true");
        return true;
    }

    // 释放资源
    public synchronized void removeLock(Object a, Object b) {
        lockList.remove(a);
        lockList.remove(b);
    }
}

新的转账接口和测试代码:

package exercise_01_0705_synchronized;

public class AccountNew {
    private Integer balance = 10000; // 账户余额
    // 转账操作
    void transfer(AccountNew account2, int amt) throws InterruptedException {
        while (!Allocator.getAllocator().getLock(this, account2)) {
            Thread.sleep(1000);
        }
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " this lock");
            synchronized (account2) {
                System.out.println(Thread.currentThread().getName() + " account2 lock");
                account2.balance += amt;
                this.balance -= amt;
                System.out.println(Thread.currentThread().getName() + " account2 unlock");
            }
            System.out.println(Thread.currentThread().getName() + " this unlock");
        }
        Allocator.getAllocator().removeLock(this, account2);
    }

    public static void main(String[] args) {
        AccountNew account = new AccountNew();
        AccountNew account2 = new AccountNew();
        Thread a = new Thread(()->{
            try {
                account.transfer(account2,100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread b = new Thread(()->{
            try {
                account2.transfer(account,90);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        a.start();
        b.start();
    }
}

运行结果:解决了死锁问题哦
在这里插入图片描述

3.3 破坏不可抢占条件(synchronized 做不到)

synchronized 申请资源的时候,如果没有申请到,则会线程阻塞,并不会释放已有资源。
其他lock是可以做到的(了解后回来补充,嘻嘻)。

3.4 破坏循环等待条件

对资源进行排序,然后按顺序申请资源即可。
代码实现:

package exercise_01_0705_synchronized;

public class AccountSequence {
    private Integer id;
    private Integer balance = 10000; // 账户余额
    // 转账操作
    void transfer(AccountSequence accountSequence, int amt) throws InterruptedException {
        AccountSequence left = this;
        AccountSequence right = accountSequence;
        if (left.id > right.id) {
            left = accountSequence;
            right = this;
        }

        synchronized (left) {
            System.out.println(Thread.currentThread().getName() + " this lock");
            synchronized (right) {
                System.out.println(Thread.currentThread().getName() + " account2 lock");
                accountSequence.balance += amt;
                this.balance -= amt;
                System.out.println(Thread.currentThread().getName() + " account2 unlock");
            }
            System.out.println(Thread.currentThread().getName() + " this unlock");
        }
    }

    public static void main(String[] args) {
        AccountSequence accountSequence = new AccountSequence();
        accountSequence.id = 2;
        AccountSequence accountSequence2 = new AccountSequence();
        accountSequence2.id = 1;
        Thread a = new Thread(()->{
            try {
                accountSequence.transfer(accountSequence2,100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread b = new Thread(()->{
            try {
                accountSequence2.transfer(accountSequence,90);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        a.start();
        b.start();
    }
}

运行结果:避免了死锁哟
在这里插入图片描述

四、总结

要想不死锁,要么控制资源顺序,要么打包一起申请。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值