设计一个程序,内含10个线程和20个银行账户。每个线程执行100次任意账户对任意账户的转账。
具体代码实现如下。
最开始写的时候,同时锁了转入转出账户导致了死锁的产生。比如存在AB两个账户,线程1先锁了A然后尝试锁B,此时线程2先锁了B然后尝试锁A。最终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;
        // 初始值 100元
        int balance = 100;

        public Account(int index) {
            this.index = index;
        }
    }
    // java 入口点
    public static void main(String[] args) throws InterruptedException {
        new Test().run();
    }

    public void run() throws InterruptedException {
        // 源自类  通过CAS实现并发
        AtomicInteger sum = new AtomicInteger(0);
        // 20个账户
        List<Account> accounts = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            accounts.add(new Account(i));
        }
        // 线程池 总共10个
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 计数器 累计执行1000次后往下走
        CountDownLatch latch = new CountDownLatch(1000);
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Pay(accounts, latch, sum));
        }

        latch.await();

        // 合计下最终所有账户的余额 如果输出2000则ok
        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
                // 随机2个账户
                int a = new Random().nextInt(20);
                int b = new Random().nextInt(20);
                // 计数器+1
                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) {
                        ac.balance = ac.balance - money;
                    } else {
                        money = 0;
                    }
                }
                // 锁转入账户  转入和转出账户单独锁,一起锁的话会触发死锁
                if (money != 0) {
                    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;
                    }
                }
                System.out.println("解锁bc " + ac.index + " " + Thread.currentThread().getName());
                System.out.println("解锁ac " + ac.index + " " + Thread.currentThread().getName());
                System.out.println("当前执行次数" + sum.getAndIncrement());
            }
        }
    }
}

 

届ける言葉を今は育ててる
最后更新于 2021-08-26