java 模拟银行交易 死锁的解决
设计一个程序,内含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()); } } } }