一文带你学会AQS和并发工具类的关系

  AQS(AbstractQueuedSynchronizer)是JAVA中众多锁以及并发工具的基础,其底层采用乐观锁,大量使用了CAS操作, 并且在冲突时,采用自旋方式重试,以实现轻量级和高效地获取锁。

  提供一个框架,用于实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(semaphore等)。 此类旨在为大多数依赖单个原子int值表示状态的同步器提供有用的基础。 子类必须定义更改此状态的受保护方法,并定义该状态对于获取或释放此对象而言意味着什么。 鉴于这些,此类中的其他方法将执行所有排队和阻塞机制。 子类可以维护其他状态字段,但是相对于同步,仅跟踪使用方法getState,setState和compareAndSetState操作的原子更新的int值。

  此类支持默认独占模式和共享模式之一或两者。 当以独占方式进行获取时,其他线程尝试进行的获取将无法成功。 由多个线程获取的共享模式可能(但不一定)成功。 该类不“理解”这些差异,当共享模式获取成功时,下一个等待线程(如果存在)还必须确定它是否也可以获取。 在不同模式下等待的线程共享相同的FIFO队列。 通常,实现子类仅支持这些模式之一,但例如可以在ReadWriteLock发挥作用。 仅支持独占模式或仅支持共享模式的子类无需定义支持未使用模式的方法。

2. 核心知识点 2.1 state private volatile int state; // 同步状态

  state是整个工具的核心,通常整个工具都是在设置和修改状态,很多方法的操作都依赖于当前状态是什么。由于状态是全局共享的,一般会被设置成volatile类型,为了保证其修改的可见性;

2.2 CLH队列

  AQS中的队列是CLH变体的虚拟双向队列(FIFO),AQS是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配。队列采用的是悲观锁的思想,表示当前所等待的资源,状态或者条件短时间内可能无法满足。因此,它会将当前线程包装成某种类型的数据结构,扔到一个等待队列中,当一定条件满足后,再从等待队列中取出。

2.3 CAS操作

  CAS操作是最轻量的并发处理,通常我们对于状态的修改都会用到CAS操作,因为状态可能被多个线程同时修改,CAS操作保证了同一个时刻,只有一个线程能修改成功,从而保证了线程安全。CAS采用的是乐观锁的思想,因此常常伴随着自旋,如果发现当前无法成功地执行CAS,则不断重试,直到成功为止。

3. 核心实现原理 3.1 作为同步器的基础

  要将此类用作同步器的基础,请使用getState,setState或compareAndSetState检查或修改同步状态,以重新定义以下方法(如适用):

tryAcquire

独占方式,arg为获取锁的次数,尝试获取资源,成功则返回True,失败则返回False。

tryRelease

独占方式,arg为释放锁的次数,尝试释放资源,成功则返回True,失败则返回False。

tryAcquireShared

共享方式,arg为获取锁的次数,尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

tryReleaseShared

共享方式,arg为释放锁的次数,尝试释放资源,如果释放后允许唤醒后续等待结点返回True,否则返回False。

isHeldExclusively

该线程是否正在独占资源。只有用到Condition才需要去实现它。

  默认情况下,这些方法中的每一个都会引发UnsupportedOperationException 。 这些方法的实现必须在内部是线程安全的,并且通常应简短且不阻塞。 定义这些方法是使用此类的唯一受支持的方法。 所有其他方法都被声明为final方法,因为它们不能独立变化。

3.2 同步状态state

  AQS中维护了一个名为state的字段,意为同步状态,是由volatile修饰的,用于展示当前临界资源的获锁情况。

private volatile int state;

下面提供了几个访问这个state字段的方法:
返回同步状态的当前值。 此操作具有volatile读取的内存语义

protected final int getState() { return state; }

设置同步状态的值。 此操作具有volatile写操作的内存语义。

protected final void setState(int newState) { state = newState; }

  如果当前状态值等于期望值,则以原子方式将同步状态设置为给定的更新值。 此操作具有volatile读写的内存语义

protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }

  这几个方法都是Final修饰的,说明子类中无法重写它们。我们可以通过修改State字段表示的同步状态来实现多线程的独占模式和共享模式
state的值即表示了锁的状态,state为0表示锁没有被占用,state大于0表示当前已经有线程持有该锁,这里之所以说大于0而不说等于1是因为可能存在可重入的情况。你可以把state变量当做是当前持有该锁的线程数量。

public abstract class AbstractOwnableSynchronizer protected AbstractOwnableSynchronizer() { private transient Thread exclusiveOwnerThread; protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; } protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; } }

exclusiveOwnerThread 属性的值即为当前持有锁的线程独占模式获取锁流程:

图片

共享模式获取锁流程:

图片

3.3 数据结构

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdspw.html