AbstractOwnableSynchronizer
最顶级的父类 AbstractOwnableSynchronizer 内只有一个成员属性 exclusiveOwnerThread,以及它的 get、set 方法
AbstractQueuedSynchronizer
同步器可供子类重写的方法,且必须重写
方法名称 | 方法描述 |
---|---|
boolean tryAcquire(int arg) | 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态 |
boolean tryRelease(int arg) | 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态 |
int tryAcquireShared(int arg) | 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败 |
boolean tryReleaseShared(int arg) | 共享式释放同步状态 |
boolean isHeldExclusively() | 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占 |
定义的一些公共方法
方法名称 | 方法描述 |
---|---|
void acquire(int arg) | 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回 否则,将会进入同步队列等待,该方法将会调用重写的 tryAcquire(int arg)方法 |
void acquireInterruptibly(int arg) | 与 acquire(int arg)相同,但是该方法响应中断,当前线程未获取到 同步状态而进入同步队列中,如果当前线程被中断,则该方法会抛出Interruptedexception并返回 |
tryAcquireNanos(int arg, long nanosTimeout) | 在 acquireinterruptibly(int arg)基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将会返回 false,如果获取到了返回true |
void acquireShared(int arg) | 共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态 |
void acquireSharedInterruptibly(int arg) | 与 acquireShared(int arg)相同,该方法响应中断boolean tryAcquireSharedNanos(int arg, long nanosTimeout) 在 acquireSharedinterruptibly(int arg) 基础上增加了超时限制 |
boolean release(int arg) | 独占式的释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒 |
boolean releaseShared(int arg) | 共享式的释放同步状态 |
Collection getQueuedThreads() | 获取等待在同步队列上的线程集合 |
同步器提供的模板方法基本上分为3类:
- 独占式获取与释放同步状态
- 共享式获取与释放
- 同步状态和查询同步队列中的等待线程情况
Node
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;
/** 线程等待状态 */
volatile int waitStatus;
/** 前驱结点 */
volatile Node prev;
/** 后置结点 */
volatile Node next;
/** 持有的线程对象 */
volatile Thread thread;
/** 对于独占模式而言,指向下一个处于 CONDITION 等待状态的结点;对于共享模式而言,则为 SHARED 结点 */
Node nextWaiter;
// ... 省略方法定义
}
由上述字段定义可以看出,位于 CLH 链表中的线程以 2 种模式在等待资源,即 SHARED 和 EXCLUSIVE,其中 SHARED 表示共享模式,而 EXCLUSIVE 表示独占模式。共享模式与独占模式的主要区别在于,同一时刻独占模式只能有一个线程获取到资源,而共享模式在同一时刻可以有多个线程获取到资源。典型的场景就是读写锁,读操作可以有多个线程同时获取到读锁资源,而写操作同一时刻只能有一个线程获取到写锁资源,其它线程在尝试获取资源时都会被阻塞。
AQS 的 CLH 锁为处于 CLH 链表中的线程定义了 4 种状态,包括 CANCELLED、SIGNAL、CONDITION,以及 PROPAGATE,并以 Node#waitStatus 字段进行记录。这 4 种状态的含义分别为:
- CANCELLED :表示当前线程处于取消状态,一般是因为等待超时或者被中断,处于取消状态的线程不会再参与到竞争中,并一直保持该状态。
- SIGNAL:表示当前结点后继结点上的线程正在等待被唤醒,如果当前线程释放了持有的资源或者被取消,需要唤醒后继结点上的线程。
- CONDITION :表示当前线程正在等待某个条件,当某个线程在调用了 Condition#signal 方法后,当前结点将会被从条件队列转移到同步队列中,参与竞争资源。
- PROPAGATE :处于该状态的线程在释放共享资源,或接收到释放共享资源的信号时需要通知后继结点,以防止通知丢失。
一个结点在被创建时,字段 Node#waitStatus 的初始值为 0,表示结点上的线程不位于上述任何状态。
队列
节点是构成同步队列的基础,同步器拥有首节点(head) 和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部。同步器包含了两个节点类型的引用,一个指向头节点,而另一个指向尾节点。试想一下,当一个线程成功地获取了同步状态(或者锁),其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node expect,Node update),它需要传递当前线程“认为”的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。同步器将节点加入到同步队列的过程如下图所示:
同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点,该过程如下图所示:
上图中,设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置头节点的方法并不需要使用CAS来保证,它只需要将首节点设置成为原首节点的后继节点并断开原首节点的next引用即可。
ConditionObject
ConditionObject是AQS中定义的内部类,实现了Condition接口,ConditionObject是基于Lock实现的,在其内部通过链表来维护等待队列(条件队列)。Contidion必须在lock的同步控制块中使用,调用Condition的signal方法并不代表线程可以马上执行,signal方法的作用是将线程所在的节点从等待队列中移除,然后加入到同步队列中,线程的执行始终都需要根据同步状态(即线程是否占有锁)
定义的变量
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;