永远都要把wait()放到循环语句里面!

匿名 (未验证) 提交于 2019-12-02 23:26:52

今天在网上看到一段代码,是模拟银行转账的,如何保证多次转账并发执行的时候,转出账户和转入账户的金额一致。

代码可谓巧妙绝伦!先看代码:

 public class MyLock {     public static void main(String[] args) {         // 模拟转出和转入账户         Account src = new Account(100000);         Account target = new Account(100000);          // 设置倒计时         CountDownLatch countDownLatch = new CountDownLatch(99999);         for (int i = 0; i < 99999; i++) {             Thread t1 = new Thread(() -> {                 // 每次转1元钱,共转9999次                 src.transactionToTarget(1, target);                 countDownLatch.countDown();             });              t1.setName("Thread: " + i);             t1.start();         }          try {             // 等待所有线程执行完毕             countDownLatch.await();         } catch (InterruptedException e) {             e.printStackTrace();         }          // 打印账户余额         System.out.println("src: " + src.getBanalce());         System.out.println("target: " + target.getBanalce());      }      /**      * 账户类      */     static class Account {         // 余额         private Integer banalce;          public Account(Integer banalce) {             this.banalce = banalce;         }          /**          * 转账          */         public void transactionToTarget(Integer money, Account target) {             Allocator.getInstance().apply(this, target);              this.banalce = this.banalce - money;             target.setBanalce(target.getBanalce() + money);              Allocator.getInstance().release(this, target);         }          public Integer getBanalce() {             return banalce;         }          public void setBanalce(Integer banalce) {             this.banalce = banalce;         }     }      /**      * 账户管理器      */     static class Allocator {         private Allocator() {         }          // 账户锁         private List<Account> locks = new ArrayList<>();          /**          * 为转出账户和转入账户申请锁。          *          * @param src          * @param target          */         public synchronized void apply(Account src, Account target) {             while (locks.contains(src) || locks.contains(target)) {                 try {                     this.wait();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }              locks.add(src);             locks.add(target);         }          /**          * 释放锁。          *          * @param src          * @param target          */         public synchronized void release(Account src, Account target) {             locks.remove(src);             locks.remove(target);             this.notifyAll();         }          public static Allocator getInstance() {             return AllocatorSingle.install;         }          static class AllocatorSingle {             public static Allocator install = new Allocator();         }     } } 

有几个关键点:

1、为了在转账前,同时获得转出账户和转入账户的锁,设计了Allocator这个账户的管理器。

它就像一个账户管理员,拿着所有账户的账本。当有申请者来申请账户锁的时候,他会控制转入账户和转出账户同时存在;如果有其他线程当前正在操作这两个账户(正在执行账户的转入或转出),则当前线程就开始wait;否则,就让当前线程获得这两个账户的锁。

2、上面的代码里,wait放在了while里,而不能改成if!

原因:

模拟执行:假设有A,B,C三个线程,A线程先执行apply()方法,while检查为false,成功获得锁后开始执行。

如果此处的while改成if:因为C执行完apply()方法后,释放了apply()方法的操作权,

所以,几乎所有的java书籍都会建议开发者永远都要把wait()放到循环语句里面

这是前车之鉴,不是没有道理的。

文章来源: https://blog.csdn.net/qq_26898645/article/details/89064594
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!