Java 新手 求解 死锁要怎么处理

2021-07-21 19:05:38 +08:00
 coderstory

题目是 10 个线程 对应 20 个账号 每个线程执行 100 次 任意 2 个账号之间的金额交易

代码实现中遇到了死锁

假设线程 A 对 账户 A 账户 B 执行交易 ( A 转钱到 B ) 线程 B 对账户 B 和账户 A 执行交易( B 转钱到 A )

我代码写的是先锁一个账号 判断账号余额是否足够,然后锁另一个账户 最后执行转账

但是同时有 2 个线程执行了 2 个相同账户的反向操作时,就会死锁了。

package cn.bobmao.logic.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class Test {
    class Account {
        int index;
        int balance = 100;

        public Account(int index) {
            this.index = index;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new Test().run();
    }

    public void run() throws InterruptedException {
        AtomicInteger sum = new AtomicInteger(0);
        List<Account> accounts = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            accounts.add(new Account(i));
        }
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CountDownLatch latch = new CountDownLatch(1000);
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Pay(accounts, latch, sum));
        }

        latch.await();


        int count = 0;
        for (Account account : accounts) {
            count += account.balance;
        }

        System.out.println(count);


    }

    class Pay implements Runnable {
        List<Account> accounts;
        CountDownLatch latch;
        AtomicInteger sum;

        public Pay(List<Account> accounts, CountDownLatch latch, AtomicInteger sum) {
            this.accounts = accounts;
            this.latch = latch;
            this.sum = sum;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                int money = new Random().nextInt(100);
                // a->b
                int a = new Random().nextInt(20);
                int b = new Random().nextInt(20);
                latch.countDown();
                if (a == b) {
                    System.out.println("当前执行次数" + sum.getAndIncrement());

                    continue;
                }
                Account ac = accounts.get(a);
                synchronized (ac) {
                    System.out.println("锁 ac " + ac.index + " " + Thread.currentThread().getName());
                    if (ac.balance >= money) {
                        Account bc = accounts.get(b);
                        synchronized (bc) {
                            System.out.println("锁 bc " + ac.index + " " + Thread.currentThread().getName());
                            ac.balance = ac.balance - money;
                            bc.balance = bc.balance + money;
                            //latch.countDown();
                        }
                        System.out.println("解锁 bc " + ac.index + " " + Thread.currentThread().getName());
                    }
                }
                System.out.println("解锁 ac " + ac.index + " " + Thread.currentThread().getName());
                System.out.println("当前执行次数" + sum.getAndIncrement());
            }
        }
    }
}


2299 次点击
所在节点    Java
9 条回复
kekxv
2021-07-21 19:17:28 +08:00
先锁支出的,拿走,解锁,再锁接收的
lakehylia
2021-07-21 19:28:15 +08:00
扣完钱就可以解锁转出账户了,然后再尝试锁转入账户加钱。
zhgg0
2021-07-21 20:07:07 +08:00
所有线程按同一个顺序加锁。比如你这个情况,可以按照账号 id 的顺序加锁。
不管是账号 A 转给账号 B,还是账号 B 转给账号 A ;两个线程都是先锁账号 A,再锁账号 B 。
yogogo
2021-07-22 07:53:32 +08:00
转账交易,AB 两个账号都应该同时加锁
lff0305
2021-07-22 08:09:48 +08:00
帐号 a,b 排个序,加锁小的,再加锁大的,try 转账,finally 解锁大的,解锁小的 肯定没死锁
no1xsyzy
2021-07-22 09:06:56 +08:00
哲学家就餐问题
wqhui
2021-07-22 09:41:11 +08:00
两个账号一起锁了
yuk1no
2021-07-22 10:21:27 +08:00
直接 stm
MoHen9
2021-07-22 22:09:57 +08:00
不管是 A 给 B 转账,还是 B 给 A 转账,逻辑都是一样的。
扣钱时锁被扣钱的账户,加钱时锁被加钱的账户。

两个独立账户没必要一起上锁,只要遇到错误可回退就可以,而且各自的账户可以用各自的锁,也不用争同一个锁。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/790916

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX