2019-07-10

场景练习一:

转账功能

描述:
小新转100给小明,同时小红转50给小新
原有账户余额


image.png

代码初版设计
转账功能:

private static void transfer(Account from, Account to, BigDecimal amount) {
        if (null == from || null == to || null == amount) {
            log.info("【转账】失败.参数不正确");
        }
        synchronized (from) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                log.error("异常", e);
            }
            synchronized (to) {
                from.setBalance(from.getBalance().subtract(amount));

                to.setBalance(to.getBalance().add(amount));

            }
        }
    }

主函数调用

        Account xiaoming = new Account("1", BigDecimal.valueOf(100), "小明");
        Account xiaohong = new Account("2", BigDecimal.valueOf(200), "小红");
        Account xiaoxin = new Account("3", BigDecimal.valueOf(300), "小新");

        System.out.println("小明转账前:" + xiaoming.toString());
        System.out.println("小红转账前:" + xiaohong.toString());
        System.out.println("小新转账前:" + xiaoxin.toString());

        List> results = new ArrayList<>();
        results.add(executor.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                transfer(xiaoxin, xiaoming, BigDecimal.valueOf(100));
                return 1;
            }
        }));
        results.add(executor.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                transfer(xiaohong, xiaoxin, BigDecimal.valueOf(50));
                return 1;
            }
        }));

        for (Future result : results) {
            try {
                result.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("------------------------------");
        System.out.println("小明转账后:" + xiaoming.toString());
        System.out.println("小红转账后:" + xiaohong.toString());
        System.out.println("小新转账后:" + xiaoxin.toString());

结果:

小明转账前:Account(id=1, balance=100, name=小明)
小红转账前:Account(id=2, balance=200, name=小红)
小新转账前:Account(id=3, balance=300, name=小新)
------------------------------
小明转账后:Account(id=1, balance=200, name=小明)
小红转账后:Account(id=2, balance=150, name=小红)
小新转账后:Account(id=3, balance=250, name=小新)

看起来好像没啥问题,可是会造成死锁!!!
设想,如果是两个线程,线程A跑小新转给小明100,线程B跑小明转给小新50.这样线程A先获取小新这个账户锁,同时线程B获取小明这个账户锁,双方都拿了对方需要的锁,这样就相处等待⌛️

所以呢,要改进一下,改进方法很多,可以采用lock获取锁失败直接拒绝策略。或者也可以按照一定顺序加锁,这样大家加锁策略就都一致啦~~
优化版:

private static void transferSuccess(Account from, Account to, BigDecimal amount) {
        if (null == from || null == to || null == amount) {
            log.info("【转账】失败.参数不正确");
        }

        Account min = from.getId().compareTo(to.getId()) > 0 ? to : from;
        Account max = from.getId().compareTo(to.getId()) > 0 ? from : to;

        synchronized (min) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (max) {
                from.setBalance(from.getBalance().subtract(amount));

                to.setBalance(to.getBalance().add(amount));
            }
        }
    }

你可能感兴趣的:(2019-07-10)