zoukankan      html  css  js  c++  java
  • ReentrantLock加锁解锁过程

    公平锁

    调用lock方法加锁

    进入acquire方法获取加锁的许可

     进入tryacquire

     首先获取当前线程和status状态,status默认为0

    假如现在t1线程进入,然后t2线程进入(t2进入时t1还没有释放锁)

    if c==0成立,然后判断是否需要排队,调用hasqueuedpredecessors方法

     此时的头和尾都是null,此方法返回false,所以上面if(!hasqueuedpredecessors())成立,然后进行cas操作,将status改为1

    然后设置持有锁的是当前线程。最后返回true。即t1拿到锁继续执行自己的业务逻辑。。。。

    1、如果t2执行lock方法的时候t1已经释放锁,也就不会存在竞争,一次执行。

    2、如果t1还没有释放锁,t2也会走上面的代码。

    走到tryacquire方法尝试去获取锁的时候肯定失败,因为t1还在占用。方法返回false。

     然后会执行addwriter方法进入队列排队。

     首先创建一个节点node,节点里包含属性thread,pre,next,是一个双向链表

    由于tail=null,所以会走enq方法。

     此处无限循环,t==null也成立,首先会先初始化一个新的Node,node里的信息目前为空。然后设置为头部。(其实队列里的头部永远都是一个空节点,空节点的意思是有node对象,只不过里面的thread属性为空)

    继续循环,走else,里面的逻辑意思是将自己的节点设置为尾部,空节点设置为头部,然后将空节点的next指向t2节点,t2的pre指向头部的空节点。

    最终的关系如下图

    继续走acquirequeued方法

     1 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
     2         int ws = pred.waitStatus;//等待状态,默认为0,Node.SIGNAL=-1
     3         if (ws == Node.SIGNAL)
     4             /*
     5              * This node has already set status asking a release
     6              * to signal it, so it can safely park.
     7              */
     8             return true;
     9         if (ws > 0) {
    10             /*
    11              * Predecessor was cancelled. Skip over predecessors and
    12              * indicate retry.
    13              */
    14             do {
    15                 node.prev = pred = pred.prev;
    16             } while (pred.waitStatus > 0);
    17             pred.next = node;
    18         } else {
    19             /*
    20              * waitStatus must be 0 or PROPAGATE.  Indicate that we
    21              * need a signal, but don't park yet.  Caller will need to
    22              * retry to make sure it cannot acquire before parking.
    23              */
    24             compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    25         }
    26         return false;
    27     }

    所以会走到compareAndSetWaitStatus方法将上一个节点的waitstatus改为-1,然后返回false,由于acquirequeued方法里是for(;;)

    所以会继续执行shouldParkAfterFailedAcquire,此时的waitstatus=-1,返回true。

    这个时候就是所谓的自旋,自旋了两次。

    返回true之后根据上面代码的逻辑会走parkandcheckinterrupt方法

     调用park方法,此时t2开始阻塞。知道t1释放锁。

    此时t3如果也进入lock方法。

    如果t1还没有释放锁, 和t2同样的逻辑。直到t3也阻塞。

    如果此时t1已经释放锁,那么会是t2拿到锁。因为入队之后,会尝试一次加锁许可的过程中,会判断当前节点是不是第一个排队的节点(也就是他的上一个节点是不是头部)

    如果是才有资格去获取锁,因为前面还有t2,所以t3会继续排队,t2被唤醒之后,会将自己节点的thread属性赋值null,next指向t3,t3的pre指向t2。

    然后把头部指向t2,尾部指向t3。空节点的next=null。

    此时最先初始化的那个空节点已经没有任何引用。等待被回收。源码里有体现。

    非公平锁

     调用lock方法,直接就就行cas操作。

    unlock解锁过程

     

     简单几步:1、将status设置为0

    2、设置持有当前锁的线程为null

    3、唤醒队列里waitstatus不等于0的节点,调用unpark方法。

     简单总结:调用lock方法,存在竞争的时候,T2会去入队,首先会初始化一个空节点,t2节点实际上存放的是第二个位置,t3进来的时候继续在后面排队,

     t2和t3都是调用park方法进行阻塞。入队的时候会将前面的节点的waitstatus状态由0改为-1。在调用unlock的时候会将waitstatus不等于0的释放。

  • 相关阅读:
    python测试开发django-115.Paginator分页器展示table表格数据
    python面试题-如"上海 深圳 深圳 上海",要求输入一个匹配模式,比如: aabb,判断是否符合
    python测试开发django-114.ModelForm中局部钩子(clean_)和全局钩子校验
    python测试开发django-113.使用Bootstrap框架
    MySQL将查询的结果作为update更新的数据,且在原字段数据后 CONCAT拼接(lej)
    MongoDB和Redis的区别是什么
    【精选】由浅入深带你吃透MQ原理与应用场景
    mysql 往表中某个字段的字符串后追加字符串
    mongodb 安装及使用
    Redis和MongoDB的区别(面试受用)
  • 原文地址:https://www.cnblogs.com/hkdpp/p/11917383.html
Copyright © 2011-2022 走看看