当前位置:   article > 正文

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

java银行转账并发问题

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/404444
推荐阅读
相关标签
  

闽ICP备14008679号