关于AQS的源码解析,原本是没有打算特地写一篇文章来介绍的。不过在写本学期课程做业中,有一门写了关于AQS的,并且也画了一些相关的图,因此直接拿过来分享一下,若有错误欢迎指正。
而后基本简介也都不介绍了,网上一大堆,这里就直接进行源码的分析了。java
AQS属性简介:node
属性 | 类型 | 详解 |
---|---|---|
Head | Node类型 | 持有锁的线程结点,也是队列中的头结点 |
Tail | Node类型 | 阻塞队列中的尾结点,同时每个新的结点进来,都插入到阻塞队列的最后。 |
State | int类型 | 大于等于0。表明当前锁的状态。0表明没有线程占用当前锁,大于0表明有线程持有锁。 |
exclusiveOwnerThread(继承自AOS) | Thread类型 | 表明独占锁的线程。 |
AQS的具体结构以下图所示:
ui
在AQS链表中,将每个线程包装成Node实例,并经过链表的形式连接保存,在链式结构中,节点经过next和prev分别与前驱节点和后置节点相链接。其中head节点表示为当前持有锁的线程,不在阻塞队列中。tail节点为链表中最后一个节点,当有新的节点被添加到链表中后,AQS会将tail引用指向最后一个被添加进链表的节点。this
Node属性简介:线程
字段 | 简介 | 字段 | 简介 |
---|---|---|---|
SHARE | 标识节点当前在共享模式下 | EXCLUSIVE | 标识节点当前在独占模式下 |
CANCELLED | 标识当前节点所表示的线程已取消抢锁 | SIGNAL | 标识当前节点须要在释放锁后唤醒后继节点 |
CONDITION | 与ConditionObject内部类有关 | waitStatue | 取值为以上几种状态 |
prev | 表明当前节点的前驱节点 | next | 表明当前节点的后继节点 |
thread | 表明当前节点所表示的线程 |
这里以一个锁的具体使用方法对AQS类进行详细的分析:
code
首先,线程先对锁对象进行获取操做,若是当前须要获取的锁对象并无其余线程所持有,成功获取到了锁,将执行相关的业务代码,执行完毕后,对锁资源进行释放,以便其余线程所使用。若是当前线程获取锁资源失败,说明锁资源有其余线程在使用,当前线程将进行阻塞状态,等待再次获取锁资源。对象
以java.util.concurrent.locks.ReentrantLock.java
文件中的公平锁为例:blog
abstract static class Sync extends AbstractQueuedSynchronizer #java.util.concurrent.locks.ReentrantLock中第220行 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); #调用了AQS中的方法 } ... } ================AQS==================== #java.util.concurrent.locks.AbstractQueuedSynchronizer中第1197行 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
在lock()
方法中,线程首先尝试抢锁tryAcquire(1)
,若是抢锁成功则直接返回true
,表明当前线程已持有锁资源,不然返回false
,进行下一次抢锁动做。
当线程抢锁失败后,AQS将将当前线程封装成Node
节点,并添加到阻塞队列。以后将从阻塞队列中依次取出等待锁的Node
节点,并再次尝试获取锁.若是再次获取锁失败,则使当前线程本身中断本身。继承
首先获取锁的状态,判断当前是否有线程持有锁,这里分为两种状况:队列
若是当前并无线程持有锁资源,则判断阻塞队列中是否有节点排在当前节点的前面等待获取锁资源。这里分为两种状况:
流程图以下:
源码:
#java.util.concurrent.locks.ReentrantLock中第231行 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
在线程获取锁以前,首先判断阻塞队列中是否有其余节点,若是有其余节点则放弃抢锁。
首先获取AQS链表中的头节点与尾节点,分别进行判断:
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第1512行 public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
若是当前线程抢锁失败则经过AQS将当前线程包装成Node节点添加进阻塞队列。
将当前线程以独占锁的模式包装成Node节点。
入队操做结束将当前节点返回。
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第605行 private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
这一步将当前节点添加到阻塞队列中。
首先获取阻塞队列中的尾节点,判断是否为空,有两种状况:
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第583行 private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
到达这一步说明节点已进入阻塞队列,节点尝试获取锁或者进行挂起操做。
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第857行 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
当线程暂时获取不到锁资源时,判断是否应该挂起当前线程。
首先获取当前节点的前驱节点的状态,这里有三种状况:
* 前驱节点的状态为SIGNAL。其中,SIGNAL代表该节点在释放锁资源后应该将后置节点唤醒。返回true。
* 前驱节点的状态为CANCELLED。CANCELLED代表该节点已取消抢锁,此时将从当前节点开始向前寻找,直到找到一个节点的状态不为CANCELLED,而后将他设置为当前节点的前驱节点。以后返回false.
* 若是前驱节点的状态不是以上两种状况,则经过CAS将前驱节点的状态设置为SIGNAL,以后返回false。
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第795行 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) return true; if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
将当前线程挂起,当线程被唤醒后将线程的中断状态返回.
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第835行 private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
尝试释放锁资源,这里有两种状况:
流程图以下:
源码:
#java.util.concurrent.locks.ReentrantLock中第456行 public void unlock() { sync.release(1); } ============================== #java.util.concurrent.locks.AbstractQueuedSynchronizer中第1260行 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
当持有锁的节点执行相关代码完成后,须要释放锁资源并唤醒后置节点。
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第638行 private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); 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); }
当线程因为异常或某些特殊状况的发生,须要取消对锁资源的获取,将执行取消抢锁操做。
流程图以下:
源码:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第742行 private void cancelAcquire(Node node) { if (node == null) return; node.thread = null; Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; Node predNext = pred.next; node.waitStatus = Node.CANCELLED; if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; // help GC } }
其实到这里还有一些内容并无分析完,之后再补上好了。