什么是AQS
- AbstractQueuedSynchronizer是一个队列同步器,是用来构建锁和其它同步组件的基础框架,它使用一个volatile修饰的int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程排队的工作
- 通过改变int成员变量state来表示锁是否获取成功,当state>0表示锁获取成功,当state=0时说明锁释放成功。提供了三个方法(
getState()
、setState(int newState)
、compareAndSetState(int expect,int update)
)来对同步状态state进行操作,AQS确保对state操作时线程安全的。 - 主要使用方式是继承,子类通过继承同步器并实现它的抽像方法来管理同步状态。
- 提供独占式和共享式两种方式来操作同步状态的获取与释放
- ReentrantLock、ReentrantReadWriteLock、Semaphore等就并发工具就是基于护一个内部帮助器类集成AQS来实现的的
AQS提供的方法(列出主要几个)
acquire(int arg)
以独占模式获取对象,忽略中断。acquireInterruptibly(int arg)
以独占模式获取对象,如果被中断则中止。acquire(int arg)
以独占模式获取对象,忽略中断。acquireShared(int arg)
以共享模式获取对象,忽略中断。acquireSharedInterruptibly(int arg)
以共享模式获取对象,如果被中断则中止。compareAndSetState(int expect, int update)
如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。getState()
返回同步状态的当前值。release(int arg)
以独占模式释放对象。releaseShared(int arg)
以共享模式释放对象。setState(int newState)
设置同步状态的值。tryAcquire(int arg)
试图在独占模式下获取对象状态。tryAcquireNanos(int arg, long nanosTimeout)
试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。tryAcquireShared(int arg)
试图在共享模式下获取对象状态。tryAcquireSharedNanos(int arg, long nanosTimeout)
试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。tryReleaseShared(int arg)
试图设置状态来反映共享模式下的一个释放。
队列同步器的实现分析
- 同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
1 | static final class Node { |
节点是构成同步队列的基础,同步器拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部,同步队列的
同步器的acquire方法(获取)
1 | public final void acquire(int arg) { |
- 调用自定义同步器实现的tryAcquire(int arg)方法,该方法保证线程安全的获取同步状态,如果同步状态获取失败,则构造同步节点并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部,最后调用acquireQueued(Node node,int arg)方法,使得该
节点以”死循环”(自旋)的方式获取同步状态。
独占式的获取与释放总结
- 在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自旋)的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点
同步器的release方法(释放)
1 | public final boolean release(int arg) { |
- 该方法执行时,会唤醒头节点的后继节点线程,unparkSuccessor(Node node)方法使用LockSupport来唤醒处于等待状态的线程。
基于AQS实现一个简单的可重入的独占式锁的获取与释放
1 | package com.example.juc; |
测试
1 | public class MyAQSLockTest { |