写的有点乱,因为无论是ReadWriteLock还是ReentrantLock加锁解锁过程本身就很乱。 只写了lock方法,lockInterruptibly方法其实和lock差不多,只是再睡眠的时候被打断了醒来后发现被打断了就抛出了一个异常,其余没什么差别。 首先读写锁也是有公平和非公平之分。这一点要注意。默认是非公平锁。
aqs经典方法
private Node addWaiter(Node mode) { //new 出一个node, Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure //拿到aqs的尾节点。 Node pred = tail; //如果尾节点不为null,说明aqs已经被初始化 if (pred != null) { //就是节点指向,链表维护。 node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //如果尾节点为null,或者cas失败(表示同一个时间点,多个线程cas执行,只成功一个) //往下看enq方法 enq(node); return node; }aqs经典方法
private Node enq(final Node node) { for (;;) { //拿到尾节点 Node t = tail; //如果为空,则说明队列未初始化 if (t == null) { // Must initialize //初始化队列。 if (compareAndSetHead(new Node())) tail = head; } else { //这个和上一个的入队操作一样,注意这个地方是死循环,哪怕几千个 //线程同时cas去抢资源,无线循环也会入队完成。 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }之前这个node已经入队了。
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //拿到node的前一个节点 final Node p = node.predecessor(); //如果上一个节点是aqs的头节点,再尝试加锁一次,避免park,真牛这代码 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } //第一次shouldParkAfterFailedAcquire为false,继续来一次。 //第二次p.waitstatus变成-1,则准备park了。 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //解锁成功后,不管是不是false还是true都进入下一轮循环。 //去尝试加锁,去竞争。 interrupted = true; } } finally { if (failed) cancelAcquire(node); } }aqs的方法之一
public final boolean release(int arg) { //把state置为0之后 if (tryRelease(arg)) { //拿到头节点 Node h = head; //如果头节点的waitStatus != 0 就unpark他下一个节点。 //这里注意,waitStatus 初始化为0,如果不为0,说明 //有第二个线程把他置为-1 或者什么什么的,说明有人排队 if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }aqs方法之一
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) 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; //这里我也不清楚为什么循环,可能他只是想waitStatus<0的节点。 //我猜测之前waitStatus再某个地方某个逻辑(可能是取消的时候)设置成>0了。 //果然顺着源码去读,验证猜测。 /** waitStatus value to indicate thread has cancelled static final int CANCELLED = 1;*/ for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } // s != null 就去唤醒他 if (s != null) LockSupport.unpark(s.thread); }