美文网首页
2019-07-10

2019-07-10

作者: 可乐zzz | 来源:发表于2019-07-10 19:12 被阅读0次

    场景练习一:

    转账功能

    描述:
    小新转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<Future<Integer>> results = new ArrayList<>();
            results.add(executor.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    transfer(xiaoxin, xiaoming, BigDecimal.valueOf(100));
                    return 1;
                }
            }));
            results.add(executor.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    transfer(xiaohong, xiaoxin, BigDecimal.valueOf(50));
                    return 1;
                }
            }));
    
            for (Future<Integer> 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

          本文链接:https://www.haomeiwen.com/subject/jzdkkctx.html