AQS

AbstractQueuedSynchronizer又称为队列同步器(后面简称AQS),它是用来构建锁或其他同步组件的基础框架,内部通过一个int类型的成员变量state来控制同步状态,当state=0时则说明没有任何线程占有共享资源的锁,当state=1时则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待,AQS内部通过内部类Node构成FIFO的同步队列来完成线程获取锁的排队工作,同时利用内部类ConditionObject构建等待队列,当Condition调用wait()方法后,线程将会加入等待队列中,而当Condition调用signal()方法后,线程将从等待队列转移动同步队列中进行锁竞争。两种队列,一种同步队列,当线程请求锁而等待的后将加入同步队列等待,而另一种则是等待队列(可有多个),通过Condition调用await()方法释放锁后,将加入等待队列。

  1. /**
  2. * AQS抽象类
  3. */
  4. public abstract class AbstractQueuedSynchronizer
  5. extends AbstractOwnableSynchronizer{
  6. //指向同步队列队头
  7. private transient volatile Node head;
  8. //指向同步的队尾
  9. private transient volatile Node tail;
  10. //同步状态,0代表锁未被占用,1代表锁已被占用
  11. private volatile int state;
  12. //省略其他代码......
  13. }

head为空结点,不存储信息。tail则是同步队列的队尾,同步队列采用的是双向链表的结构这样可方便队列进行结点增删操作。
state变量则是代表同步状态,执行当线程调用lock方法进行加锁后,如果此时state的值为0则说明当前线程可以获取到锁,同时将state设置为1表示获取成功。如果state已为1,也就是当前锁已被其他线程持有,那么当前执行线程将被封装为Node结点加入同步队列等待。其中Node结点是对每一个访问同步代码的线程的封装。每个Node结点内部关联其前继结点prev和后继结点next,这样可以方便线程释放锁后快速唤醒下一个在等待的线程。

Node是AQS的内部类,其数据结构如下:

  1. static final class Node {
  2. //共享模式,一个锁允许多条线程同时操作
  3. static final Node SHARED = new Node();
  4. //独占模式,同一个时间段只能有一个线程对共享资源进行操作
  5. static final Node EXCLUSIVE = null;
  6. //标识线程已处于结束状态
  7. static final int CANCELLED = 1;
  8. //等待被唤醒状态
  9. static final int SIGNAL = -1;
  10. //条件状态,
  11. static final int CONDITION = -2;
  12. //在共享模式中使用表示获得的同步状态会被传播
  13. static final int PROPAGATE = -3;
  14. //等待状态,存在CANCELLED、SIGNAL、CONDITION、PROPAGATE 4种
  15. volatile int waitStatus;
  16. //同步队列中前驱结点
  17. volatile Node prev;
  18. //同步队列中后继结点
  19. volatile Node next;
  20. //请求锁的线程
  21. volatile Thread thread;
  22. //等待队列中的后继结点,这个与Condition有关,稍后会分析
  23. Node nextWaiter;
  24. //判断是否为共享模式
  25. final boolean isShared() {
  26. return nextWaiter == SHARED;
  27. }
  28. //获取前驱结点
  29. final Node predecessor() throws NullPointerException {
  30. Node p = prev;
  31. if (p == null)
  32. throw new NullPointerException();
  33. else
  34. return p;
  35. }
  36. //.....
  37. }

image.png

AQS对于锁的实现存在两种不同的模式,即共享模式(如Semaphore)和独占模式(如ReetrantLock),无论是共享模式还是独占模式的实现类其内部都是基于AQS实现的。
pre和next,分别指向当前Node结点的前驱结点和后继结点,thread变量存储的请求锁的线程。
变量waitStatus则表示当前被封装成Node结点的等待状态,共有4种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE

  • CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化。
  • SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。
  • CONDITION:值为-2,与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。
  • PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态。
  • 0状态:值为0,代表初始化状态。



image.png
image.png