Java并发学习三:银行转账的死锁问题解决及示例

本文介绍了在Java并发编程中,如何处理银行转账可能导致的死锁问题。通过分析死锁的四个必要条件,选择破坏"占有且等待"条件,使用Allocator类管理临界区,实现资源的申请和释放。当获取锁失败时,采用阻塞等待的方式避免死锁,利用Java提供的`tryLock()`函数进行等待通知。
摘要由CSDN通过智能技术生成

Java并发学习系列文章:Java并发学习-博客专栏


今天在学习极客时间专栏:《Java并发编程实战》
03 | 互斥锁(上):解决原子性问题06 | 用“等待-通知”机制优化循环等待

课程主要用银行转账作为解决死锁的例子。

在转账过程中需要2把锁,一个锁锁住转出账户的余额,一把锁锁住转入账户的余额。但加锁的先后顺序会使程序产生死锁。


有以下这四个条件都发生时才会出现死锁:
1.互斥,共享资源 X 和 Y 只能被一个线程占用;
2.占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
3.不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
4.循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。


我们选择破坏占有且等待这个条件使死锁失效。“同时申请”这个操作是一个临界区,我们也需要一个角色(Java 里面的类)来管理这个临界区,我们就把这个角色定为 Allocator。它有两个重要功能,分别是:同时申请资源 apply() 和同时释放资源 free()。账户 Account 类里面持有一个 Allocator 的单例(必须是单例,只能由一个人来分配资源)

现在有了另外一个问题,获取锁失败的线程应该怎么做,常见的做法有:
1.死循环尝试获取锁
2.轮询
3.进入阻塞状态,等待通知。


最好的办法自然是进入阻塞状态,等待通知。Java提供函数waitnotifynotifyAll


所以完善后的完整测试代码如下:

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import static java.lang.System.exit;

//相当于临界区,规避死锁
class Allocator {
   
    //单例设计模式的饿汉式
    static private final Allocator instance = new Allocator();
    private Set<Object> res = new HashSet<>();

    public static Allocator getInstance() {
   
        return instance;
    }

    public synchronized void getLock(Object from, Object to) {
   
        while (res.contains(from) || res.contains(to)) {
   
            try {
   
                //将不满足条件的线程阻塞,加入等待的
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值