ReentrantLock释放锁流程

一、ReentrantLock释放锁过程

ReentrantLock的unlock()方法不区分公平锁还是非公平锁。

  • 首先调用unlock()方法。

  • unlock()底层使用的是Sync.release(1)方法

      public void unlock() {
         sync.release(1);
     }
    
  • release(1)方法会调用tryRelease(1)去尝试解锁。

       public final boolean release(int arg) {
       //尝试释放锁
         if (tryRelease(arg)) {
             Node h = head;
             if (h != null && h.waitStatus != 0)
                //如果释放锁成功,而且等待队列不为空,且有一个以上的等待线程
                //因为只有下一个线程才能将前一个线程的waitStatus的状态改为-1,head表示当前执行的线程
                //当head不为空,且waitStatus !=0说明有等待线程初始化了等待队列,且将持有锁线程的
                //等待状态改为了-1,必然存在等待线程,将队头的第一个唤醒
                 unparkSuccessor(h);
             return true;
         }
         return false;
     }
    
  • tryRelease(arg)尝试释放锁

    @ReservedStackAccess
        protected final boolean tryRelease(int releases) {
            //释放一次锁,就将重入的次数减掉1
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果锁得状态为1,则表示锁真正被释放了,将持有锁的线程置为null
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //否则,锁依然被持有,因为该锁被持锁线程重入了多次
            setState(c);
            return free;
        }
    
  • 如果tryRelease()释放锁成功,且判断等待队列确实有阻塞线程,则尝试唤醒

     private void unparkSuccessor(Node node) {
        //如果等待的线程状态<0,SIGNAL,将其设为0
         int ws = node.waitStatus;
         if (ws < 0)
             node.compareAndSetWaitStatus(ws, 0);
         Node s = node.next;
         //找一个符合条件,即真正在阻塞睡眠的线程
         if (s == null || s.waitStatus > 0) {
             s = null;
             for (Node p = tail; p != node && p != null; p = p.prev)
                 if (p.waitStatus <= 0)
                     s = p;
         }
         //找到后,将其唤醒。有个疑问,头节点不变化嘛???
         if (s != null)
             LockSupport.unpark(s.thread);
     }
    
    

    回答自己的疑问,为啥没有操作头节点呢?这是因为唤醒阻塞的第一个线程后,它会重新去获取锁,而不是直接将锁分配给它。

        final boolean acquireQueued(final Node node, int arg) {
         boolean interrupted = false;
         try {
             for (;;) {
                 final Node p = node.predecessor();
                 if (p == head && tryAcquire(arg)) {
                     setHead(node);
                     p.next = null; // help GC
                     return interrupted;
                 }
                 if (shouldParkAfterFailedAcquire(p, node))
                 //从此处被唤醒后,重新进行循环,尝试去争抢锁,如果没抢到,则继续阻塞(非公平的时候)
                 //当刚被唤醒,循环一次,此时p==head,同时如果tryAcquire(1)去获得锁,
                 //如果获得成功将自己设置为head,
                 //如果获得锁失败,则自己再自旋一次(因为在释放锁的时候,head的ws又重置为0了).
                 //如果还是失败,则自己再次park()睡眠
                     interrupted |= parkAndCheckInterrupt();
             }
         } catch (Throwable t) {
             cancelAcquire(node);
             if (interrupted)
                 selfInterrupt();
             throw t;
         }
     }
    

二、流程图

借用一下大佬的(比我考虑的多太多了):

在这里插入图片描述

引用:https://blog.csdn.net/nrsc272420199/article/details/105442016

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值