重入锁(2)

书接上文创建线程节点并且加入队列
//=========================
在这里插入图片描述

addWaiter方法返回一个thread 为当前线程的一个节点,将此节点传入acquireQueued方法

在这里插入图片描述
首先进入死循环,新建node p为当前节点的前一个节点,如果p是head,则表示当前节点在队列中排第一,于是再次去执行tryAcquire方法,这个if是一次自旋;如果p不是head,则说明当前节点之前还有节点在排队,于是进入下一个If语句。
在这里插入图片描述
获得上一个节点的状态值waitStatus
在这里插入图片描述
节点的构造方法如图,放在队列里的节点都是由addWaiter构造的,addWaiter采用的是第二种构造方法,所以waitStatus为默认值0。
接下来判断ws值是否为SIGNAL,在Node 类中能看到 ,SIGNAL默认值为-1。
在这里插入图片描述
所以直接跳过前两个if,进入else,compareAndSetWaitStatus()将上一个节点的ws值改成-1,reuturn false到doAcquireInterruptibly()方法中,使&&之后的方法不被执行,然后再次循环,再次判断上一个是不是head。若还拿不到锁,就再进
shouldParkAfterFailedAcquire()方法,这一次ws = -1,return true。&&之后的方法被执行,进入parkAndCheckInterrupt()。

==========================================
ws = -1 表示当前线程阻塞,当线程没有被阻塞时不能改成-1,当线程被阻塞了自己没办法改成-1,所以需要下一个线程改成-1。

尝试两次的原因一是为了尽量避免阻塞(为什么ws=0时不直接返回true),二是 ws = 0 是一个必要的状态不能随便改(为什么不在第一次进就把ws改成-1)。
//===================================
//=================================
在这里插入图片描述
这个方法里会调用park方法,使线程的许可(premit)-1,许可默认是0,当许可为0或1(最大为1)时可以运行,-1时被阻塞。于是线程被阻塞在LockSupport.park()方法。
//======================

/接下来进入解锁方法===================

reentrantlock.unlock();

在这里插入图片描述

arg=1,传入tryRelease中。

protected final boolean tryRelease(int releases) {
             /*锁被持有时>=1,
             作为重入锁其状态码在
             每次调用unlock时应当减去1。*/
            int c = getState() - releases;
            /*持有锁的线程是否为当前线程,此歩判断是为了防止
            线程中没写lock直接写了unlock。
           */
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            /*锁被持有且没有重入情况,
            则将其设置为自由,当前持有线程设置为null。*/
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            /*将锁的state设置为c,
            有重入情况return false表示解锁失败,
            没有就return true 表示解锁成功  */        
            setState(c);
            return free;
        }

如果解锁成功返回true到release方法,进入if语句;获取头节点记为h,如果h为null,即阻塞队列不存在,则直接返回true表示解锁成功。如果头节点不为空,并且头节点的waitStatus不为0,则表示头节点后边有节点在排队,于是进入unparkSuccessor()方法
在这里插入图片描述
当前ws=-1,进入此方法后首先会将ws重新设置为0,然后使节点s表示头节点的下一个节点,如果s不为空或者状态>0(设置为大于0就是为了此时跳过该大于0的节点),调用 LockSupport.unpark(s.thread),唤醒队列中正在排队的第一个线程。

如果为空就从队尾开始一直向前遍历找到第一个ws = -1或0的节点。
(如果出现 前一个线程释放锁成功后还没返回true后一个线程刚刚将前一个的ws改为-1后自旋获得锁成功 时间重叠 的情况,会导致前一个线程也进入unparkSuccessor(),这就是为什么t.waitStatus<=0而不是单纯的小于0,并且,头节点就是当前拿锁线程的节点,因此不存在需要将上一个节点弹出队列,并且改变head.next以保证头节点不变的问题

并将其唤醒。唤醒后回到

在这里插入图片描述
判断线程是否被打断,return false,进入acquireQueued,再次死循环,拿到锁,将拿到锁的线程设置为head,将p的next设置为null,拆除引用,使其快速地被JVM回收,将failed设置为false。
在这里插入图片描述

failed默认为true,当线程得到锁之后failed才会被设置成false,被唤醒的线程刚刚得到了锁,所以cancelAcquire不会被执行。可以看到拿锁成功后是返回false的,因此可以一路顺利返回到线程当中继续执行,如果返回true,则代表有中断,还需执行其他相关中断的方法,不在本文讨论之列。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值