ReentrantLock 学习笔记

有篇写的很不错的博客: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方法是个死循环,直到头节点释放锁的时候,删除头节点跳出循环.(会调用

LockSupport.park()挂起当前线程,进行阻塞)

最后:关于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的尾节点,直到该节点获得锁,执行后面的语句!

原文地址:https://www.cnblogs.com/jinjian91/p/9194254.html