• 捞鱼的搬砖奇
    2019-03-09
    synchronized(Account.class) 锁了Account类相关的所有操作。相当于文中说的包场了,只要与Account有关联,通通需要等待当前线程操作完成。while死循环的方式只锁定了当前操作的两个相关的对象。两种影响到的范围不同。

    作者回复: 还真是这样啊!

     5
     51
  • Tony Du
    2019-03-09
    while循环是不是应该有个timeout,避免一直阻塞下去?

    作者回复: 你考虑的很周到!👍
    加超时在实际项目中非常重要!

     2
     49
  • Demon.Lee
    2019-03-09
    while(actr.apply(this, target)); --> while(!actr.apply(this, target));
    我感觉应该是这样,老师,我理解错了?

    作者回复: 你发现了个大bug!感谢感谢!!!我这就修改一下啊

    
     41
  • 张立华
    2019-03-12
    之前遇到死锁,我就是用资源id的从小到大的顺序去申请锁解决的

    作者回复: 这个方案最简单

     1
     33
  • 几字凉了秋丶
    2019-03-10
    老师,请问一下,在实际的开发中,account对象应该是从数据库中查询出来的吧,假如A转B,C转B一起执行,那B的account对象如何保证是同一个对象,不太理解。。。

    作者回复: 实际开发中都是用数据库事务+乐观锁的方式解决的。这个就是个例子,为了说明死锁是怎么回事,以及死锁问题怎么解决。

    
     23
  • 轻歌赋
    2019-03-09
    存在性能差距,虽然申请的时候加锁导致单线程访问,但是hash判断和赋值时间复杂度低,而在锁中执行业务代码时间长很多。
    申请的时候单线程,但是执行的时候就可以多线程了,这里性能提升比较明显

    想问问老师,如何判断多线程的阻塞导致的问题呢?有什么工具吗

    作者回复: 可以用top命令查看Java线程的cpu利用率,用jstack来dump线程。开发环境可以用 java visualvm查看线程执行情况

    
     17
  • 别皱眉
    2019-03-14
    @阿官 我来回答下你的问题

    以下是阿官的问题
    -------------------------------------------------------
    老师,在破坏占用且等待的案例中,为何申请完两个账户的资源后还需要再分别锁定this和target账户呢?
    -------------------------------------------------------
    因为还存在其他业务啊 比如客户取款
    这个时候也是对全局变量balance做操作
    如果不加锁 并发情况下会出问题

    老师你看我说的对吗😄😄
    展开

    作者回复: 你说到我心里了😃😃😃

     3
     16
  • 邋遢的流浪剑客
    2019-03-09
    思考题的话希望老师能够过后给出一个比较标准的答案,毕竟大家的留言中说法各不相同很难去判断答案的对错

    作者回复: 这一部分的最后一章,要不就给答案吧

    
     12
  • 李可威
    2019-03-17
    老师为什么按序申请资源就可以破坏循环等待条件呢?这点没有看懂求解答

    作者回复: 循环等待,一定是A->B->C->...->N->A形成环状。
    如果按需申请,是不允许N->A出现的,只能N->P。没有环状,也就不会死锁了。

     1
     10
  • gogo
    2019-03-10
    看了老师的讲解学到了很多,联想了下实际转账业务,应该是数据库来实现的,假如有账户表account,利用mysql的悲观锁select ...for update对a,b两条数据锁定,这时也有可能发生死锁,按照您讲到的第三种破坏循环等待的方式,按照id的大小顺序依次锁定。我这样理解的对吗?

    作者回复: 是的,就是id的次序。

    
     9
  • Howie
    2019-03-09
    while 循环就是一个自旋锁机制吧,自旋锁的话要关注它的循环时间,不能一直循环下去,不然会浪费 cpu 资源。

    作者回复: 自旋锁在JVM里是一种特殊的锁机制,自诩不会阻塞线程的。咱们这个其实还是会阻塞线程的。不过原理都一样,你这样理解也没问题。

    
     8
  • 陈华
    2019-03-14
    对于第三点,按资源顺序来锁就能打破循环等待有疑问。
    例如:账户 1 向 账户 3 转账
      同时 账户 3 向 账户 5 转账
    即使按资源顺序来锁,也是起不了啥作用吧!?,

    作者回复: 能起作用,这俩操作不会死锁

     2
     7
  • GP
    2019-03-13
    问下,上节最后说到,不能用可变对象做锁,这里为何又synchronized(left)?

    作者回复: 保护的是对象里面的成员,这俩对象变也只能是里面成员变,相对于里面的成员来说,这俩对象是永远不会变的。你可以这样理解。不是绝对不能用于可变对象,只是一条最佳实践。

    
     7
  • 王二宝
    2019-08-28
    最常见的就是B转A的同时,A转账给B,那么先锁B再锁A,但是,另一个线程是先锁A再锁B,然而,如果两个线程同时执行,那么就是出现死锁的情况,线程T1锁了A请求锁B,此时线程T2锁了B请求锁A,都在等着对方释放锁,然而自己都不会释放锁,故死锁。
    最简单的办法,就是无论哪个线程执行的时候,都按照顺序加锁,即按照A和B的id大小来加锁,这样,无论哪个线程执行的时候,都会先加锁A,再加锁B,A被加锁,则等待释放。这样就不会被死锁了。

    作者回复: 👍

    
     6
  • aguan(^・ェ・^)
    2019-03-14
    老师,在破坏占用且等待的案例中,为何申请完两个账户的资源后还需要再分别锁定this和target账户呢?

    作者回复: 为了保险而已,单纯这个例子是不需要的,如果还有取款操作就需要了

    
     6
  • 长眉_张永
    2019-03-13
    关键是如何找到最合适的锁的力度。

    作者回复: 是啊,所以知识只是知识,不是能力

    
     6
  • Bright丶
    2019-04-26
    老师,感觉下面的代码也能避免死锁,并且能实现功能:
    void transfer(Account target, int amt){
        boolean isTransfer = false;
    // 锁定转出账户
        synchronized(this){
              if (this.balance > amt) {
              this.balance -= amt;
              isTransfer = true;
        }
        if (!isTransfer) {
             return;
        }
          // 锁定转入账户
          synchronized(target){
              target.balance += amt;
          }
      }

    反映到现实中的场景:服务员A拿到账本1先判断余额够不够,够的话先扣款,再等待其他人操作完账本2,才增加它的额度。

    但是这样转账和到账就存在一个时差,现实生活中也是这样,转账不会立马到账,短信提醒24小时内到账,所谓的最终一致性。

    老师帮忙看看这样实现会不会有啥其他问题?
    展开

    作者回复: 实际工作中也有这么做的,只不过是把转入操作放到mq里面,mq消费失败会重试,所以能保证最终一致性。

    
     5
  • Nero.t.Kang
    2019-03-11
    虽然看起来 while(!actr.apply(this, target));只是锁住了两个对象,但是因为actr是一个单例的对象,这个方法在执行的时候也需要锁住actr,在多线程状态下也相当于是串行化了,那么这和加上一个Account.class的类锁的串行化有什么区别吗?请老师赐教,谢谢。

    作者回复: 有区别,如果转账操作很耗时,那么a-b,c-d能并行还是有价值的

    
     5
  • 叶十七
    2019-09-18
    如果有人没有理解透彻,看着例子来写生产代码,那么并发情况下会出问题,如果并发小,一直没出问题,会以为代码没问题,真正出问题的时候都分析不出来哪里错了。

    并发情况下,这些代码的加锁对象并不是同一个,所以是有问题的。-- 不同的线程都获取到了账户A的实例对象,但这些实例对象不是同一个。

    希望所有读者都能看透这个,多线程对账户A,B实例加锁时一定要保证是同一个实例对象,就像在数据库表中通过select * from account where account_id = ? for update 加锁一样,锁住的是同一条账户记录。
    展开
    
     4
  • λ
    2019-03-11
    单例导致操作也是串行的吧

    作者回复: 是串行,但是允许A转B,C转D

     1
     4
我们在线,来聊聊吧