•  
    2019-03-30
    我觉得:不会出现死锁,但会出现活锁

    作者回复: 👍

     1
     39
  • bing
    2019-03-30
    文中说的公平锁和非公平锁,是不按照排队的顺序被唤醒,我记得非公平锁的场景应该是线程释放锁之后,如果来了一个线程获取锁,他不必去排队直接获取到,应该不会入队吧。获取不到才进吧

    作者回复: 是的,高手👍👍👍

    
     33
  • xiyi
    2019-03-30
    存在活锁。这个例子可以稍微改下,成功转账后应该跳出循环。加个随机重试时间避免活锁

    作者回复: 👍👍👍

    
     32
  • 小华
    2019-03-30
    有可能活锁,A,B两账户相互转账,各自持有自己lock的锁,都一直在尝试获取对方的锁,形成了活锁

    作者回复: 👍

     1
     27
  • Q宝的宝
    2019-03-30
    老师,本文在讲述如何保证可见性时,分析示例--“线程 T1 对 value 进行了 +=1 操作后,后续的线程 T2 能否看到 value 的正确结果?“时,提到三条Happen-Before规则,这里在解释第2条和第3条规则时,似乎说反了,正确的应该是,根据volatile变量规则,线程T1的unlock()操作Happen-Before于线程T2的lock()操作,所以,根据传递性规则,线程 T1 的 value+=1操作Happen-Before于线程T2的lock()操作。请老师指正。

    作者回复: 火眼金睛👍👍👍👍,这就改过来

     1
     13
  • 刘晓林
    2019-03-30
    1.这个是个死循环啊,有锁没群,都出不来。
    2.如果抛开死循环,也会造成活锁,状态不稳定。当然这个也看场景,假如冲突窗口很小,又在单机多核的话,活锁的可能性还是很小的,可以接受

    作者回复: 👍👍👍

    
     12
  • 姜戈
    2019-03-30
    我也觉得是存在活锁,而非死锁。存在这种可能性:互相持有各自的锁,发现需要的对方的锁都被对方持有,就会释放当前持有的锁,导致大家都在不停持锁,释放锁,但事情还没做。当然还是会存在转账成功的情景,不过效率低下。我觉得此时需要引入Condition,协调两者同步处理转账!

    作者回复: 用condition会更复杂

    
     9
  • 海鸿
    2019-03-30
    突然有个问题:
    cpu层面的原子性是单条cpu指令。
    java层面的互斥(管程)保证了原子性。
    这两个原子性意义应该不一样吧?
    我的理解是cpu的原子性是不受线程调度影响,指令要不执行了,要么没执行。而java层面的原子性是在锁的机制下保证只有一个线程执行,其余等待,此时cpu还是可以进行线程调度,使运行中的那个线程让出cpu时间,当然了该线程还是掌握锁。
    我这样理解对吧?
    展开

    作者回复: 对

    
     8
  • linqw
    2019-04-07
    class Account {
      private int balance;
      private final Lock lock
              = new ReentrantLock();
      // 转账
      void transfer(Account tar, int amt){
        boolean flag = true;
        while (flag) {
          if(this.lock.tryLock(随机数,NANOSECONDS)) {
            try {
              if (tar.lock.tryLock(随机数,NANOSECONDS)) {
                try {
                  this.balance -= amt;
                  tar.balance += amt;
         flag = false;
                } finally {
                  tar.lock.unlock();
                }
              }//if
            } finally {
              this.lock.unlock();
            }
          }//if
        }//while
      }//transfer
    }
    感觉可以这样操作
    展开

    作者回复: 点个大大的赞!不过还可以再优化一下,如果阻塞在tar.lock.tryLock上一段时间,this.lock是不能释放的。

     3
     7
  • 羊三@XCoin.AI
    2019-03-30
    用非阻塞的方式去获取锁,破坏了第五章所说的产生死锁的四个条件之一的“不可抢占”。所以不会产生死锁。

    用锁的最佳实践,第三个“永远不在调用其他对象的方法时加锁”,我理解其实是在工程规范上避免可能出现的锁相关问题。

    作者回复: 是的

    
     7
  • 朱小豪
    2019-03-30
    应该是少了个break跳出循环,然后这个例子是会产生死锁的,因为满足了死锁产生的条件。

    作者回复: 加了break,也会有活锁问题,不加的话我觉得也是活锁,因为锁都会释放

    
     6
  • 江湖夜雨
    2019-07-28
    终于理解最后一道题出现活锁问题了,两个线程同事执行,线程A获取了u1.trylock,线程B获取了u2.trylock,线程A尝试获取u2.trylock,不能成功,线程A结束,同时线程B尝试获取u1.trylock,不能成功,线程B结束,又开始新的一轮,这样一直循环下去!
    
     5
  • Liam
    2019-03-30
    1 不会出现死锁,因为不存在阻塞的情况
    2 线程较多的情况会导致部分线程始终无法获取到锁,导致活锁

    作者回复: 👍

    
     5
  • 小予
    2019-07-17
    1、转账成功跳出循环,避免死循环
    2、锁自己账户时,加一个随机等待时间,避免活锁
    3、锁目标账户时,锁不到则直接解锁
    class Account {
        private int balance;
        private final Lock lock = new ReentrantLock();
        // 转账
        void transfer(Account tar, int amt){
            while (true) {
             if(this.lock.tryLock(随机数,NANOSECONDS)) {
                    try {
                        if (tar.lock.tryLock()) {
                            try {
                                this.balance -= amt;
                                tar.balance += amt;
                                // 转账成功结束循环
                                break;
                            } finally {
                                tar.lock.unlock();
                            }
                        }//if
                    } finally {
                        this.lock.unlock();
                    }
                }//if
            }//while
        }//transfer
    }
    展开
     1
     4
  • tdytaylor
    2019-05-15
    老师,关于这个问题,我思考之后觉得不会出现死锁,但是没看出为什么会出现活锁

    作者回复: 想想对面相遇的两个人互相谦让的例子看看

    
     4
  • 森呢
    2019-07-04
    老师,你好,这是我第二遍研读你的课程了,每一遍都收获很大。第一次写留言有点紧张。
    你上面写的jdk利用内存模型的三条规则来保证可见性,是正确的。但我觉得好像描述的理由好像不充分,我不知道我理解的对不对,请老师解答一下
    我的理解应该是 :1)释放锁成功后,写state的值 (unlock>state-=1) 顺序性
    2)获取锁前,读state值(state>lock)顺序性
    3)传递性 unlock>lock

    下面是jdk的源码
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();//获取当前线程实例
        int c = getState();//获取state变量的值,即当前锁被重入的次数
        if (c == 0) { //state为0,说明当前锁未被任何线程持有
            if (compareAndSetState(0, acquires)) { //以cas方式获取锁
                setExclusiveOwnerThread(current); //将当前线程标记为持有锁的线程
                return true;//获取锁成功,非重入
            }
        }
        else if (current == getExclusiveOwnerThread()) { //当前线程就是持有锁的线程,说明该锁被重入了
            int nextc = c + acquires;//计算state变量要更新的值
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);//非同步方式更新state值
            return true; //获取锁成功,重入
        }
        return false; //走到这里说明尝试获取锁失败
    }

    展开

    作者回复: 感谢补充😄state必须是volatile变量,否则是不会有unlock>lock的,我比你更紧张😂

     1
     3
  • 静水流深
    2019-08-23
    以前学并发编程时候很吃力,就是坚持学完了本章后,我有种豁然开朗的感觉!谢谢老师!这个专栏,强烈建议涨价!!

    作者回复: 这个建议我觉得非常好😃

    
     2
  • 右耳听海
    2019-03-31
    请问state=1先读取是怎么得出来的,还有lock和unlock的方法对state都是写操作,怎么用到valiate规则的,valiate规则不是读取操作先与写操作吗,这个地方两个都是写操作

    作者回复: =1之前有一段代码会查看状态是否为0,显然不能三七二十一直接设置

    
     2
  • 朱小豪
    2019-03-30
    本文最后的例子,不明白为什么要用while true而且没有跳出循环,这不是死循环吗
    
     2
  • Wangxi
    2019-09-23
    请问老师一个问题。AQS实现的锁,阻塞唤醒线程使用的是park/unpark方法。synchronized关键字实现的锁,在底层也是park/unpark。这两种锁底层实现可以认为是一致的吗。如果是一致的。那么lock接口也是重量级锁了。

    作者回复: 最终都会调用操作系统提供的API,线程都会被挂起,所以从这个角度看都是重量级的

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