@lovely 2023-06-30 21:06 采纳率: 86.4%
浏览 22
已结题

java 可重入的公平锁

ReenLock可重入的公平锁

img

img


在执行这行代码的时候,会调用hasQueuedPredecessors这个方法,这个方法有点不懂
他们说,这个方法是判断是否有前一个节点,说这个方法也是实现公平锁的原因,
但我是一点也看不懂,这个方法里面为什么要这样进行判断,有人说这里又无法实现公平,因为它可能会被插队

  • 写回答

2条回答 默认 最新

  • SoberChina 2023-07-04 17:08
    关注

    hasQueuedPredecessors 这个判断当前线程是否位于等待队列的头结点,止于为什么判断这么复杂咱们先不谈。你没看懂的原因是有几点,第一好多逻辑运算符,第二 好多有取反的逻辑。咱们先做个假设~

    首先呢

            Node t = tail; //尾结点
            Node h = head;//头结点
            Node s;
            // 判断头是否等于尾结点(取反,有些网上解释不太严谨,后面再说。),然后判断首节点的next节点与当前线程是否一个(取反)
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
    
    

    好了 我们来解释为什么网上说的好多是不严谨的。因为AQS 中的等待队列是使用双向链表来表示的。所以说每个节点都有prev和next。当然head节点是“null”为什么呢?因为只有在存在线程持有锁的时候,后续线程加入锁竞争时候才会存在这个双向链表,严谨的标识当前head节点标识正在持有锁的节点。好了那么为什么说是为了判断等待队列中是否有元素呢,第一次尝试获取锁,比如线程a,head和tail都为null(可以理解为没有锁竞争),然后顺利的获取到锁;然后当前琐未释放,另外一个线程b进行获取锁,当前锁已被占用,开始走加入等待队列逻辑,逻辑中可以看到初始化队列的过程;此时有线程a持有锁,所以head的next节点指向了线程b,尾结点tail的prev指向线程a(其中只有next节点指向线程b其他信息均为null);假如现在线程a释放了锁,唤醒线程b开始获取锁,此时设定当前线程为head,代码如下:

    final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head && tryAcquire(arg)) {
                        //重点方法~
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
      //将线程置为空,以及前驱节点,也就是说当前线程获取到锁,然后将等待队列头指向当前节点,并将描述当前线程的信息值为空。
      private void setHead(Node node) {
            head = node;
            node.thread = null;
            node.prev = null;
        }
    

    加入多个线程加入到等待队列,只有当前线程的node.prev与当前的head相等,每次加入队列尾部,标识当前线程节点的pre为上一个线程的head。

    hasQueuedPredecessors这个方法也不是实现公平锁的关键,等待队列才是关键,该方法只是多了几个判断,多了一层保障,首先判断等待队列中是否有元素~ 如果有元素的话判断head的next线程是否是当前线程。

    其中为什么说他也不是完全公平的,首先:使用了大量的cas;其次:可重入锁,获取锁的线程可以多次获取锁,也不可能是先来后到。
    以上拙见,还望能得到帮助,如果能的话方便点一个采纳~ 谢谢。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 7月12日
  • 已采纳回答 7月4日
  • 创建了问题 6月30日

悬赏问题

  • ¥15 写代码写代码单片机代码写代码写数字按键代码
  • ¥15 django按照距离进行排序
  • ¥15 (标签-微信|关键词-微信公众号)
  • ¥15 matlab中mjs用不了
  • ¥15 Ios抖音直播的时候如何添加自定义图片在直播间!
  • ¥60 riscv-pulpino总线上挂载axi从机
  • ¥15 ssh登录页面的问题
  • ¥50 关于在matlab上对曲柄摇杆机构上一点的运动学仿真
  • ¥15 jetson nano
  • ¥15 :app:debugCompileClasspath'.