支付系统无死锁解决方案

118 阅读1分钟
package com.justalk.javademo.deadlock;

import org.jetbrains.annotations.NotNull;

import java.util.Random;

public class DemonstrateDeadLock {

    public static class InsufficientFundsException extends Exception { }

    public static class Account {
        public String accountName;
        public Account(String accountName) {
            this.accountName = accountName;
        }

        //默认账户里面有10_000美金
        DollarAmount dollarAmount = new DollarAmount(10_000);

        public DollarAmount getBalance() {
            return dollarAmount;
        }

        public void debit(DollarAmount amount) throws InterruptedException {
            System.out.println(String.format("%S减少:%d美金",accountName,amount.cash));
            dollarAmount.cash -= amount.cash;
            System.out.println(String.format("%S还有金额:%d美金",accountName,dollarAmount.cash));
            Thread.sleep(500);
        }

        public void credit(DollarAmount amount) throws InterruptedException {
            System.out.println(String.format("%S增加:%d美金",accountName,amount.cash));
            dollarAmount.cash += amount.cash;
            System.out.println(String.format("%S还有金额:%d美金",accountName,dollarAmount.cash));
            Thread.sleep(500);
        }
    }

    public static class DollarAmount implements Comparable<DollarAmount> {

        private int cash;

        public DollarAmount(int cash) {
            this.cash = cash;
        }

        @Override
        public int compareTo(@NotNull DollarAmount dollarAmount) {
            return cash - dollarAmount.cash;
        }
    }

    private static final Object tieLock = new Object();
    private static final int NUM_THREADS = 20;
    private static final int NUM_COUNT = 2;
    private static final int NUM_DO = 10_000;

    public static void main(String[] args) {
        final Random random = new Random();
        final Account[] accounts = new Account[NUM_COUNT];
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = new Account("账户"+i);
        }
        class TransferThread extends Thread {
            @Override
            public void run() {
                for (int i = 0; i < NUM_DO; i++) {
                    int fromAcct = random.nextInt(NUM_COUNT);
                    int toAcct = random.nextInt(NUM_COUNT);
                    DollarAmount amount = new DollarAmount(random.nextInt(1000));
                    try {
                        try {
                            transferMoney(accounts[fromAcct],accounts[toAcct],amount);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            System.out.println("发生了线程中断");
                        }
                    } catch (InsufficientFundsException e) {
                        System.out.println("转账金额不正确");
                        e.printStackTrace();
                    }
                }
            }

            private void transferMoney(final Account fromAcct,
                                       final Account toAcct,
                                       final DollarAmount amount)
                    throws InsufficientFundsException, InterruptedException {

                class Helper {
                    public void transfer() throws InsufficientFundsException, InterruptedException {
                        int detal = fromAcct.getBalance().compareTo(amount);
                        System.out.println(String.format("比较的差值是:%d",detal));
                        if (detal < 0) {
                            throw new InsufficientFundsException();
                        } else {
                            System.out.println(String.format("fromAcct:%s,toAcct:%s",fromAcct.accountName,toAcct.accountName));
                            fromAcct.debit(amount);
                            toAcct.credit(amount);
                        }
                    }
                }

                int fromHash = System.identityHashCode(fromAcct);
                int toHash = System.identityHashCode(toAcct);
                if (fromHash < toHash) {
                    synchronized (fromAcct) {
                        synchronized (toAcct) {
                            new Helper().transfer();
                        }
                    }
                } else if (fromHash > toHash) {
                    synchronized (toAcct) {
                        synchronized (fromAcct) {
                            new Helper().transfer();
                        }
                    }
                } else {
                    synchronized (tieLock) {
                        synchronized (fromAcct) {
                            synchronized (toAcct) {
                                new Helper().transfer();
                            }
                        }
                    }
                }
            }
        }
        for (int i = 0; i < NUM_THREADS; i++) {
            new TransferThread().start();
        }
    }

}