简述: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