以前虽然看过一次AQS的源码实现,但在过一段时间后与同学交流时,发觉自己理解并不够深,印像太浅。需要做一个记录整理,帮助自己消化。
几个点:
1. Node实现作者: "CLH" (Craig, Landin, and * Hagersten) ,有名的CLH queue
2. 是一个FIFO的链表的实现,对于队列的控制经常要做double-check。
3. Node节点通过一个int waiteStatus代表一些不同意义的状态。
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)) { // 位置1 pred.next = node; return node; } } enq(node); return node; } private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize // 位置3 Node h = new Node(); // Dummy header h.next = node; node.prev = h; if (compareAndSetHead(h)) { // 位置4 tail = node; return h; } } else { node.prev = t; if (compareAndSetTail(t, node)) { // 位置2 t.next = node; return t; } } } }
private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }
final Node p = node.predecessor(); // 位置1 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return interrupted; }
private void unparkSuccessor(Node node) { /* * Try to clear status in anticipation of signalling. It is * OK if this fails or if status is changed by waiting thread. */ compareAndSetWaitStatus(node, Node.SIGNAL, 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) { //位置1 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); }
按照原先作者的设计:
Acquire: while (!tryAcquire(arg)) { enqueue thread if it is not already queued; possibly block current thread; } Release: if (tryRelease(arg)) unblock the first queued thread;
预留了5个protected方法,用于client自己实现相关的处理,进行业务行为控制,因为cocurrent很多Lock,Future的实现都是基于此扩展,定义了自己的处理。
具体的一些方法使用,后续再补。
独占锁:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); // 位置1
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && // 位置2
parkAndCheckInterrupt()) // 位置3
interrupted = true;
}
} catch (RuntimeException ex) {
cancelAcquire(node); // 位置4
throw ex;
}
}
private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); try { for (;;) { final Node p = node.predecessor(); // 位置1 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return; } if (shouldParkAfterFailedAcquire(p, node) && // 位置2 parkAndCheckInterrupt()) break; // 位置3 } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } // Arrive here only if interrupted cancelAcquire(node); throw new InterruptedException(); // 位置4 }
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { long lastTime = System.nanoTime(); final Node node = addWaiter(Node.EXCLUSIVE); try { for (;;) { final Node p = node.predecessor(); // 位置1 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return true; } if (nanosTimeout <= 0) { // 位置2 cancelAcquire(node); return false; } if (nanosTimeout > spinForTimeoutThreshold && // 位置3 shouldParkAfterFailedAcquire(p, node)) // 位置4 LockSupport.parkNanos(this, nanosTimeout); // 位置5 long now = System.nanoTime(); // 位置6 nanosTimeout -= now - lastTime; lastTime = now; if (Thread.interrupted()) // 位置7 break; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } // Arrive here only if interrupted cancelAcquire(node); throw new InterruptedException(); // 位置8 }
public final void acquireShared(int arg)
和独占锁处理方式基本类似,来看一下核心代码:
private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); // 位置1 p.next = null; // help GC if (interrupted) selfInterrupt(); return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } }
这里设计上有点小技巧,原先思考一个共享锁的典型场景:读写锁。 一旦写锁释放,应该是唤起所有的读锁。而原先在看setHeadAndPropagate,并没有一个循环释放锁的过程。后来思考了下,采用的是一个链式释放的过程,前一个shared的锁对象释放下一个,在释放的时候继续进行tryAccquireShared控制。
一点感悟:在写并发程序时,一些传统编程的思路要有所改变。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException
这两个实现上和独占锁类似,也就是setHeadAndPropagate处理上的不同点而已。
public final boolean release(int arg)
if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false;
没啥特别好将的,一看基本也就明白了,出队列的时候,同时唤醒下一个Node。
private void cancelAcquire(Node node)
代码就不贴了,几个处理:
1. 从链表上删除cancel节点
2. 如果cancel节点是head,则尝试唤醒cancel节点的下一个节点。
几个主要方法:
Array queue; Object empty = new Object(); Object full = new Object(); // 生产者 if(queue 是否满了) full.wait() //阻塞等待 else put(queue , data) //放入数据 empty.single(); // 已经放了一个,通知一下 // 消费者 if(queue 是否空了) empty.wait() // 阻塞等待 else data = get(queue); full.single() // 已经消费了,通知一下
在整个AQS存在两种链表。 一个链表就是整个Sync Node链表,横向链表。另一种链表就是Condition的wait Node链表,相对于Sync node,它属于node节点的一个纵向链表。当纵向列表被single通知后,会进入对应的Sync Node进行排队处理。
通过这样的纵横队列,实现了ConditionObject共享lock锁数据。