1.什么事AQS?
在AQS中存在一个FIFO双向队列,队列中的节点表示被阻塞的线程,队列节点元素有4种类型, 每种类型表示线程被阻塞的原因,这四种类型分别是:
CANCELLED
: 表示该线程是因为超时或者中断原因而被放到队列中CONDITION
: 表示该线程是因为某个条件不满足而被放到队列中,需要等待一个条件,直到条件成立后才会出队SIGNAL
: 表示该线程需要被唤醒(在队列中的除了尾节点以外的其他节点(排除超时的中断的应该都是这个状态))PROPAGATE
: 表示在共享模式下,当前节点执行释放release
操作后,当前结点需要传播通知给后面所有节点
由于一个共享资源同一时间只能由一条线程持有,也可以被多个线程持有,因此AQS中存在两种模式,如下:
-
1、独占模式
独占模式表示共享状态值state每次只能由一条线程持有,其他线程如果需要获取,则需要阻塞,如JUC中的
ReentrantLock
-
2、共享模式
共享模式表示共享状态值state每次可以由多个线程持有,如JUC中的
CountDownLatch
2.AQS中的设计模式?
模板方法模式:
https://www.cnblogs.com/smallJunJun/p/10892545.html
除了通用的节点入队出队是不需要子类重写的,包括获取同步状态和释放同步状态的以整套逻辑是不需要重写的。
在AQS中,模板方法设计模式体现在其acquire()、release()
方法上,其中调用tryAcquire()
方法的默认实现是抛出一个异常,也就是说tryAcquire()
方法留给子类去实现,acquire()
方法定义了一个模板,一套处理逻辑,相关具体执行方法留给子类去实现
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //成功拿到锁的时候,acquireQueued会返回false,就不会往下执行了 selfInterrupt(); } protected boolean tryAcquire(int arg) { //这就是留给子类方法去实现的 throw new UnsupportedOperationException(); }
3.AQS中的核心数据结构和入队出队,节点的一些变化
队列中一个节点的结构:
static final class Node { /**共享模式*/ static final Node SHARED = new Node(); /**独占模式*/ static final Node EXCLUSIVE = null; /**标记线程由于中断或超时,需要被取消,即踢出队列*/ static final int CANCELLED = 1; /**线程需要被唤醒*/ static final int SIGNAL = -1; /**线程正在等待一个条件*/ static final int CONDITION = -2; /** * 传播 */ static final int PROPAGATE = -3; // waitStatus只取上面CANCELLED、SIGNAL、CONDITION、PROPAGATE四种取值之一 volatile int waitStatus; // 表示前驱节点 volatile Node prev; // 表示后继节点 volatile Node next; // 队列元素需要关联一个线程对象 volatile Thread thread; // 表示下一个waitStatus值为CONDITION的节点 Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } /** * 返回前一个节点,如果没有前一个节点,则抛出空指针异常 */ final Node predecessor() throws NullPointerException { // 获取前一个节点的指针 Node p = prev; // 如果前一个节点不存在 if (p == null) throw new NullPointerException(); else // 否则返回 return p; } // 初始化头节点使用 Node() {} /** * 当有线程需要入队时,那么就创建一个新节点,然后关联该线程对象,由addWaiter()方法调用 */ Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } /** * 一个线程需要等待一个条件阻塞了,那么就创建一个新节点,关联线程对象 */ Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
入队列:
如果来了一个线程可以直接获取同步状态,就不用进入队列,否则进入addWaiter判断,如果队列里面存在节点并且成功插入,就直接进入if循环返回插入的这个节点。队列为空,或者CAS操作失败没插入,进入enq死循环加CAS
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; }
head节点是不持有线程对象的
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; } } } }