说明

  • state表示状态, 子类具体定义状态的含义
  • 互斥模式, 不能获取已被其它线程获取的资源, 共享模式可以
  • 通过实现tryAcquire(int), tryRelease(int), tryAcquireShared(int), tryReleaseShared(int), isHeldExclusively(), 中的全部或者部分接口完成具体功能
  • 通过到hasQueuedPredecessors()的判断实现公平性

同步器是实现锁的关键,利用同步器将锁的语义实现,然后在锁的实现中聚合同步器。可以这样理解:锁的API是面向使用者的,它定义了与锁交互的公共行为,而每个锁需要完成特定的操作也是透过这些行为来完成的(比如:可以允许两个线程进行加锁,排除两个以上的线程),但是实现是依托给同步器来完成;同步器面向的是线程访问和资源控制,它定义了线程对资源是否能够获取以及线程的排队等操作。锁和同步器很好的隔离了二者所需要关注的领域,严格意义上讲,同步器可以适用于除了锁以外的其他同步设施上(包括锁)。同步器的开始提到了其实现依赖于一个FIFO队列,那么队列中的元素Node就是保存着线程引用和线程状态的容器,每个线程对同步器的访问,都可以看做是队列中的一个节点。Node的主要包含以下成员变量:

  1. Node {
  2. int waitStatus;
  3. Node prev;
  4. Node next;
  5. Node nextWaiter;
  6. Thread thread;
  7. }

以上五个成员变量主要负责保存该节点的线程引用,同步等待队列(以下简称sync队列)的前驱和后继节点,同时也包括了同步状态。

int waitStatus 表示节点的状态。其中包含的状态有:
CANCELLED,值为1,表示当前的线程被取消;
SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
值为0,表示当前节点在sync队列中
Node prev 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接
Node next 后继节点
Thread thread 入队列时的当前线程

常用操作流程图

acquire

AQS(AbstractQueuedSynchronizer) - 图1

release

AQS(AbstractQueuedSynchronizer) - 图2

这里注意的是AbstractQueuedSynchronizer是一个抽象类,定义了基本的框架。AQS核心是用一个变量state来表示状态. AQS也就是AbstractQueuedSynchronizer这个类只是定义了一个队列管理线程,对于线程的状态是子类维护的,我们可以理解为是一个同步队列,当有线程获取锁失败时(多线程争用资源被阻塞时会进入此队列),线程会被添加到队列的队尾

  • AQS只是负责管理线程阻塞队列
  • 线程的阻塞和唤醒

image.png上面的表示了队列的形态,head表示队列的头节点,tail表示队列的尾节点。在源码中他们的定义使用volatile定义的。使用volatile关键字保证了变量在内存中的可见性,详见:volatile关键字解析。保证某个线程在出队入队时被其他线程看到。

private transient volatile Node head;//头节点
private transient volatile Node tail;//尾节点

在AbstractQueuedSynchronizer这个类中还有一个内部类Node,用于构建队列元素的节点类。
在AQS中定义了两种资源共享方式

  • Exclusive:独占式
  • Share:共享式

在不同的实现类中为了实现不同的功能,会采用不同的共享方式,例如可重入锁ReentrantLock采用的就是独占锁。 AQS的不同实现类,不需要关注线程等待队列的维护和管理(线程阻塞入队、唤醒出队),在AQS中这些是已经定义好的,不同的同步器只需要对以下方法进行实现即可:

//独占方式尝试获取资源
protected boolean tryAcquire(int arg)
//独占方式尝试释放资源
protected boolean tryRelease(int arg)
//共享方式尝试获取资源,返回值0表示成功但是没有剩余资源,负数表示失败,正数表示成功且有剩余资源
protected int tryAcquireShared(int arg)
//共享方式尝试释放资源
protected boolean tryReleaseShared(int arg)

所有自定义的同步器只需要确定自己是那种资源贡献方式即可:共享式、独占式。也可以同时实现共享式和独占式ReentrantReadWriteLock读写锁,多个线程可以同时进行读操作,但是只能有一个线程进行写操作

acquire流程

image.png