ReentrantLock实现及AQS简析

匿名 (未验证) 提交于 2019-12-03 00:32:02

简述:reentrantLock是java当中提供的一个锁,他和synchronized关键字有所不同,这篇文章主要是从reentrantLock获取锁,然后释放锁,来分析整个底层源码的实现,并不介绍ReentrantLock的具体使用




我们最常使用的lock方式:创建一个非公平锁,并且添加锁操作

Lock lock = new ReentrantLock();
 lock.lock();
 public void lock() {         sync.lock();     }

调用NonfairSync代码实现

 final void lock() {             if (compareAndSetState(0, 1))                 setExclusiveOwnerThread(Thread.currentThread());             else                 acquire(1);         }

sync继承自AbstractQueuedSynchronizer类,简称AQS, 其内部维护了一个int 类型的state变量,以及当前获取到锁的线程对象thread,

以上第一步是1、将AQS当中的state状态更改为1,假设现在有三个线程来访问,其中thread1成功将state状态改为1,另外两个线程thread2 和thread3 将执行acquire(1), 以下是AQS当中acquire方法的实现:

 public final void acquire(int arg) {         if (!tryAcquire(arg) &&             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))             selfInterrupt();     }

2、接下来将线程2和线程3添加到AQS双向链表当中的队尾

tryAcquire的方法实现:

 final boolean nonfairTryAcquire(int acquires) {             final Thread current = Thread.currentThread();             int c = getState();             if (c == 0) {                 if (compareAndSetState(0, acquires)) {                     setExclusiveOwnerThread(current);                     return true;                 }             }             else if (current == getExclusiveOwnerThread()) {                 int nextc = c + acquires;                 if (nextc < 0) // overflow                     throw new Error("Maximum lock count exceeded");                 setState(nextc);                 return true;             }             return false;         }

再次判断thread1是否将锁释放掉了,如果state的值是0,并且线程2成功将状态修改成1,说明线程2获取到锁,返回true

如果当前线程和AQS当中持有的线程是同一个线程,那么就在原来state的基础上添加acquires,并且返回true

否则返回false说明尝试获取锁失败

接下来我们来看下如果获取锁失败后,是如何操作的,addWaiter方法

 private Node addWaiter(Node mode) {         Node node = new Node(Thread.currentThread(), mode);         // Try the fast path of enq; backup to full enq on failure         Node pred = tail;         if (pred != null) {             node.prev = pred;             if (compareAndSetTail(pred, node)) {                 pred.next = node;                 return node;             }         }         enq(node);         return node;     }

获取到AQS当中node的尾部元素,如果tail是null 的话,执行enq方法

private Node enq(final Node node) {         for (;;) {             Node t = tail;             if (t == null) { // Must initialize                 if (compareAndSetHead(new Node()))                     tail = head;             } else {                 node.prev = t;                 if (compareAndSetTail(t, node)) {                     t.next = node;                     return t;                 }             }         }     }

结合我们上面的场景,线程2和线程3 都没有获取到锁,那么此时tail元素是null , 在这个enq方法当中先创建一个head节点,不包含thread的,然后将thread2添加至node链表的尾部返回

接下来处理队列中的元素

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);         }     }

1、先找到当前元素的前一个元素,如果当前元素的前一个元素是head,并且tryAcquire成功了,说明当前链表的前一个元素head可以失效了

2、如果当前元素的前一个元素不是head,或者说当前线程尝试获取锁也失败了,那么执行shouldParkAfterFaildAcquire

 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {         int ws = pred.waitStatus;         if (ws == Node.SIGNAL)             /*              * This node has already set status asking a release              * to signal it, so it can safely park.              */             return true;         if (ws > 0) {             /*              * Predecessor was cancelled. Skip over predecessors and              * indicate retry.              */             do {                 node.prev = pred = pred.prev;             } while (pred.waitStatus > 0);             pred.next = node;         } else {             /*              * waitStatus must be 0 or PROPAGATE.  Indicate that we              * need a signal, but don't park yet.  Caller will need to              * retry to make sure it cannot acquire before parking.              */             compareAndSetWaitStatus(pred, ws, Node.SIGNAL);         }         return false;     }
1、如果前一个节点的waitState 是-1,直接返回,然后就是执行&&后面的操作,将当前线程挂起
private final boolean parkAndCheckInterrupt() {         LockSupport.park(this);         return Thread.interrupted();     }

2、如果前一个节点的waitState 大于0,即状态是cancelled ==1 从链表尾部开始查找,找到状态小于等于0 ,并且返回false

3、如果当前状态小于0,且不是signal状态,将pred节点的状态更新为signal




易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!