ReentrantLock 学习笔记

时间:2020-12-10 22:56:33

有篇写的很不错的博客:https://blog.csdn.net/aesop_wubo/article/details/7555956    基于JDK1.8 参考着看源码

,弄清楚lock()和unlock() 的过程.

再次基础上做下总结:

公平和非公平锁:代码层面的设计区别是,lock() 的时候,非公平锁会首先判断当前锁的状态,如果为0,则将自己设置成独占锁,不加入CLH队列

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

lock():过程,主要的方法

 
public final void acquire(int arg) {
//第一个方法判断锁的状态,addWaiter方法是添加节点,这两个比较简单,注意节点初始值waitStatus=0,Node.SIGNAL=-1
    if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
}
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;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) {//这个方法涉及到初始化状态
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {//特别要注意这个for结束的条件
final Node p = node.predecessor();//获取前一个节点
if (p == head && tryAcquire(arg)) {//若是头节点,且锁的状态是0
setHead(node);//设置该节点是头结点
p.next = null; // help GC
failed = false;
return interrupted;//只有这里结束循环
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//初始值0,释放的时候变成-1
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);//把这个状态设置成-1
}
return false;
}

unlock():过程,主要的方法

  public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)//根据这个状态去唤醒头结点
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)//设置head节点状态
compareAndSetWaitStatus(node, ws, 0); /*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
 

比如说现在有两个线程,按照上面的流程,,非公平锁的话,第一个线程先变成独占线程,第二个线程进来之后的动作---->会创建两个节点,第一个是头结点,第二个是第二个线程的节

点,默认waitStatus都 为0,shouldParkAfterFailedAcquire方法会将waitStatus变成-1,没得到锁的时候acquireQueued方法是个死循环,直到头节点释放锁的时候,删除头节点跳出循环.

最后:关于condition配合ReentrantLock使用的源码:个人觉得这位作者分析的很全:https://www.cnblogs.com/sheeva/p/6484224.html

做下补充说明:condition和ReentrantLock是有关系的.ReentrantLock自带AQS队列,condition自己也实现了个队列java.util.concurrent.locks.AbstractQueuedSynchronizer.Node,调用await()的时候会从ReentrantLock的队列中删除这个节点,并加入到condition队列,这时候condition队列CONDITION=-2,处于阻塞,直到调用single(),又把condition中该节点加入到(调用enq())ReentrantLock的尾节点,直到该节点获得锁,执行后面的语句!