有篇写的很不错的博客: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的尾节点,直到该节点获得锁,执行后面的语句!