AbstractQueuedSynchronizer

管程

 一个管程定义了一个数据结构和能够并发进程所执行的一组操作,这组操作能同步进程和改变管程中的数据。

问题及解决

AQS是一个管程,用于同步不同线程。在实现功能的过程中,需要考虑以下几个问题:

  1. 在高并发情况下如何确定当前线程是否能够进行后续操作。
  2. 如果不能进行后续操作,是否需要阻塞;如果阻塞,该如何唤醒。
  3. 如何对多个线程进行管理,是否需要实现条件等待及如何实现条件等待。

针对以上几个问题,AQS给出了解决方案:

  1. 使用一个全局的状态,通过判断全局的状态来确定线程是否可以进行后续操作。这个全局的状态为volatile的,通过使用CAS操作来实现对状态的修改,CAS相关的功能由Unsafe类提供。
  2. AQS维护一个同步队列,在同步队列中,如果线程不允许进行后续操作,则进行阻塞,阻塞操作及唤醒操作由LockSupport调用Unsafe类的方法提供。
  3. AQS维护条件队列,通过与AQS的配合,完成条件等待与唤醒操作。

结构

AQS

head:始终指向获得了锁的节点,它不会被取消。新的线程获得锁之后,之前获得锁的节点从队列中出队。

tail:负责无锁地实现一个链式结构,采用CAS+轮询的方式。节点的入队操作都在tail节点。

state:AQS当前的状态。

Node

SHARED:表示共享模式。

EXCLUSIVE:表示独占模式。

waitStatus:节点等待状态。CANCELLED=1,表示取消状态。SIGNAL=-1,表示后续节点需要被唤醒。CONDITION=-2,表示线程正处在条件等待状态。PROPAGATE=-3,表示释共享锁时,需要唤醒后续节点。

prev:指向队列中的前一个节点。

next:指向队列中的下一个节点。

thread:把一个节点关联到一个线程。

nextWaiter:指向在条件队列中的下一个正在等待的节点,是给条件队列使用的。值得注意的是条件队列只有在独享状态下使用。

ConditionObject

firstWaiter:指向条件队列的队首节点。

lastWaiter:指向条件队列的对尾节点。

工具类

LockSupport

利用Unsafe直接操作内存来存取对象的能力来设置blockers。Unsafe.objectFieldOffset可以获得某个字段在对象所在内存的offset,有了这个offset,就可以通过对象的引用来找到字段所在的实际内存地址。Thread的parkBlocker属性用来指出当前线程是在哪个对象上阻塞。park方法需要指明锁对象。park方法先setBlocker,标记当前线程是在哪个锁对象上等待,然后调用Unsafe的park方法,当Unsafe的park方法返回时表示已经退出等待,就把blockers设置为null。

Unsafe

Unsafe提供了三类与并发相关的方法:

  1. CAS方法。如compareAndSwapObject()、compareAndSwapInt()、compareAndSwapLong()。
  2. 操作条件队列的方法。如park()、unpark()。
  3. 存取volatile变量的方法。如getObjectVolatile()、putObjectVolatile()、getIntVolatile()、putIntVolatile()等。

操作

获取操作

while(状态不允许获取操作) {
    if (需要阻塞获取请求) {
        如果当前线程不在同步队列中,那么将其插入队列
        阻塞当前线程
    } else {
        返回失败
    }
}
可能更新同步器状态
如果线程位于同步队列,则将其移出队列
返回成功


释放操作
更新同步器状态
if (新的状态允许某个被阻塞的线程获取成功) { 解除队列中一个或多个线程的阻塞状态 }

方法

 

扩展

子类扩展时有两种类型,一种是公平的同步器,另一种是非公平的同步器。所谓的非公平,不是说不适用队列来维护阻塞操作,而是说在竞争时,后来的线程可以不考虑在同步队列中的线程而直接竞争资源。同步器竞争失败后,都需要进入AQS的同步队列进行等待,而同步队列是先来先服务的公平的队列。

参考资料

1.《聊聊高并发》

2.《The j.u.c Synchronizer Framework》

原文地址:https://www.cnblogs.com/qhdxqxx/p/8875853.html