AQS是什么
AbstractQueuedSynchronizer
(AQS)是Java中的一个并发工具,位于java.util.concurrent.locks
包中,用于实现基于锁的同步机制。它是许多同步类(如ReentrantLock
和Semaphore
等)的基础,并提供了一种用于实现独占锁(exclusive locks)和共享锁(shared locks)等同步机制的框架。
AQS是一个抽象类,它通过维护一个等待队列来管理线程的同步状态。它的主要设计思想是,当某个线程尝试获取锁时,如果锁不可用,该线程会被放入等待队列,然后被阻塞。当锁释放时,AQS会从等待队列中唤醒适当的线程,使其能够竞争锁。
AQS什么样
内部类
类名 | 作用 |
Node | Node 类表示等待队列中的一个节点,用于构建等待队列、实现线程的阻塞与唤醒,以及表示等待线程。 |
ExclusiveNode | ExclusiveNode 是Node 类的子类,表示独占模式(exclusive mode)下的节点,用于独占锁的等待队列。 |
SharedNode | SharedNode 是Node 类的子类,表示共享模式(shared mode)下的节点,用于共享锁的等待队列。 |
ConditionNode | ConditionNode 是Node 类的子类,表示条件等待队列中的节点,用于支持条件变量的等待和唤醒机制。 |
ConditionObject | ConditionObject 是AbstractQueuedSynchronizer 的内部类,用于实现条件变量,允许线程等待特定条件。 |
这些内部类在AbstractQueuedSynchronizer
的实现中扮演着不同的角色,从构建等待队列、表示等待线程、实现条件等待机制,到支持不同模式的锁等待。通过这些内部类的使用,AQS能够支持各种同步场景和锁的实现,从而实现多线程的协调和同步。
成员变量
先看看AQS里都有哪些成员变量:
当然,我可以为您解释这些成员变量的作用。以下是您列出的成员变量的简要解释和作用的表格展示:
变量名 | 变量类型 | 变量作用 |
WAITING | int | 表示线程处于等待状态,即等待获取锁。 |
CANCELLED | int | 表示线程在等待队列中等待时被取消,即等待被中断或其他原因取消。 |
COND | Node | 一个特殊的标识,用于表示等待队列中的节点是一个条件等待节点而不是独占或共享模式的节点。 |
head | Node | 等待队列中的头节点,即队列中等待时间最长的节点。 |
tail | Node | 等待队列中的尾节点,即队列中等待时间最短的节点。 |
state | int | 表示同步状态的变量,可以是任意整数值,根据具体实现的需要来表示不同的状态。 |
U | Unsafe | 提供了一些底层的操作,允许直接对内存进行操作,用于实现一些底层同步原语。 |
STATE | long | 一个偏移量,用于表示在AbstractQueuedSynchronizer 类中用于操作state 变量的偏移量。 |
HEAD | long | 一个偏移量,用于表示在AbstractQueuedSynchronizer 类中用于操作head 变量的偏移量。 |
TAIL | long | 一个偏移量,用于表示在AbstractQueuedSynchronizer 类中用于操作tail 变量的偏移量。 |
这些成员变量在AbstractQueuedSynchronizer
中用于维护等待队列、线程状态和同步状态等信息,从而实现了基于队列的线程同步和协调机制。不同的变量在整个机制中扮演着不同的角色,以实现正确的多线程同步行为。
方法public
我们来看一下AQS都提供了哪些方法
方法名 | 参数含义 | 方法作用 |
acquire | int arg:请求获取锁的参数 | 尝试获取锁,如果获取不到则将调用线程置于阻塞状态,直到锁可用或线程被中断。 |
acquireInterruptibly | int arg:请求获取锁的参数 | 类似于acquire ,但是允许线程在等待锁的过程中被中断。如果线程在等待时被中断,会抛出InterruptedException 异常。 |
tryAcquireNanos | int arg:请求获取锁的参数,long nanosTimeout:等待时间 | 尝试获取锁,但最多等待指定的时间。如果在超时前未能获取锁,则返回结果指示是否成功获取。 |
release | int arg:释放锁的参数 | 释放锁,通常在获取锁成功后调用。释放锁会唤醒等待队列中的其他线程,使其有机会竞争锁。 |
acquireShared | int arg:请求获取共享锁的参数 | 类似于acquire ,但是用于共享锁的获取。多个线程可以同时获取共享锁,而不像独占锁一样只能有一个线程持有。 |
acquireSharedInterruptibly | int arg:请求获取共享锁的参数 | 类似于acquireShared ,但是允许线程在等待共享锁的过程中被中断。如果线程在等待时被中断,会抛出InterruptedException 异常。 |
tryAcquireSharedNanos | int arg:请求获取共享锁的参数,long nanosTimeout:等待时间 | 类似于tryAcquireNanos ,但是用于共享锁的获取。 |
releaseShared | int arg:释放共享锁的参数 | 释放共享锁,通常在获取共享锁成功后调用。释放共享锁会唤醒等待队列中的其他线程,使其有机会竞争锁。 |
hasQueuedThreads | - | 判断是否有线程在等待队列中等待获取锁。 |
hasContended | - | 判断是否有线程在竞争锁。 |
getFirstQueuedThread | - | 获取等待队列中的第一个线程,但不移除。 |
isQueued | Thread thread:要检查的线程 | 判断指定线程是否在等待队列中等待获取锁。 |
apparentlyFirstQueuedIsExclusive | - | 判断等待队列中的第一个线程是否为独占模式(exclusive mode)线程。 |
hasQueuedPredecessors | - | 判断调用线程是否有在等待队列中的前驱线程。如果有前驱线程,则可能需要执行阻塞操作。 |
getQueueLength | - | 获取等待队列中的线程数。 |
getQueuedThreads | - | 获取在等待队列中等待获取锁的所有线程。 |
getExclusiveQueuedThreads | - | 获取在等待队列中等待获取独占锁的所有线程。 |
getSharedQueuedThreads | - | 获取在等待队列中等待获取共享锁的所有线程。 |
toString | - | 返回对象的字符串表示,通常包括等待队列中的线程信息。 |
owns | Thread thread:要检查的线程 | 判断指定线程是否是当前持有锁的线程。 |
hasWaiters | - | 判断是否有线程在等待队列中等待释放锁。 |
getWaitQueueLength | - | 获取等待队列中等待释放锁的线程数。 |
getWaitingThreads | Condition condition:相关的条件 | 获取与指定条件相关的等待线程列表。 |
这些方法是AbstractQueuedSynchronizer
类的核心方法,用于实现多线程同步和协调。
如果不使用AQS会怎样
AQS的应用
我们来直接举个例子
ReentrantLock
ReentrantLock中有3个内部类
Sync
AQS的子类,其实基本上实现了ReentrentLock的大部分方法,ReentrentLock开放出来的大部分方法其实都是直接调用的Sync里的方法
方法名 | 参数含义 | 方法作用 |
tryLock | long timeout, TimeUnit unit | 尝试获取锁,如果锁没有被其他线程持有,则获取锁并返回true,如果在指定的时间内无法获取锁,则返回false。 |
initialTryLock | - | 作为tryLock的一种形式,是在ReentrantLock的构造函数中使用的,用于初始化锁。 |
lock | - | 获取锁,如果锁已经被其他线程持有,则当前线程会被阻塞,直到获取到锁。 |
lockInterruptibly | - | 获取锁,如果锁已经被其他线程持有,允许线程在等待时被中断。如果线程在等待时被中断,会抛出InterruptedException异常。 |
tryLockNanos | long timeout | 尝试在指定的时间内获取锁,如果锁没有被其他线程持有,则获取锁并返回true。如果在指定的时间内无法获取锁,则返回false。 |
tryRelease | - | 尝试释放锁。 |
isHeldExclusively | - | 判断当前线程是否持有独占锁。 |
newCondition | - | 创建一个新的Condition对象,用于支持条件等待。 |
getOwner | - | 获取当前持有独占锁的线程,如果没有线程持有锁,返回null。 |
getHoldCount | - | 获取当前线程持有锁的次数,用于可重入锁的计数。 |
isLocked | - | 判断锁是否被任何线程持有。 |
NonfairSync
Sync的子类,非公平锁的实现。
方法名 | 参数 | 作用 |
initialTryLock | unused | 尝试获取锁,如果获取成功返回true,失败返回false。该方法只在构造方法中调用一次。 |
tryAcquire | acquires | 独占式尝试获取同步状态。如果获取成功返回true,否则返回false。 |
FairSync
Sync的子类,公平锁的实现
方法名 | 参数 | 作用 |
initialTryLock | unused | 尝试获取锁,当等待队列无线程等待并且state为0。不同于NonfairSync,公平锁只有无竞争时才会尝试获取锁。 |
tryAcquire | acquires | 尝试获取同步状态,如果队列中第一个节点是当前线程就获取成功,不是则失败。 |
FairSync也继承自AQS,但实现了公平的锁获取语义 - 等待时间最长的线程最先获得锁。
这两个类开始起作用是在创建ReentrentLock的时候,这里
其他实现
- CountDownLatch: 用来进行线程之间同步协作,可以实现一个线程等待其他线程完成某件事情之后再执行。
- CyclicBarrier: 用来进行线程之间同步协作,可以实现让一组线程达到一个屏障时被阻塞,直到最后一个线程到达屏障时屏障才会开门,所有被屏障拦截的线程才会继续执行。
- Semaphore: 用于控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
- Exchanger: 用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。
- BlockingQueue: 一个支持两个附加操作的队列。在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。
- ForkJoinPool: Fork/Join框架中的线程池实现类,用于异步执行fork/join任务。