AbstractQueuedSynchronizer

简介: AbstractQueuedSynchronizer


AQS是什么

AbstractQueuedSynchronizer(AQS)是Java中的一个并发工具,位于java.util.concurrent.locks包中,用于实现基于锁的同步机制。它是许多同步类(如ReentrantLockSemaphore等)的基础,并提供了一种用于实现独占锁(exclusive locks)和共享锁(shared locks)等同步机制的框架。

AQS是一个抽象类,它通过维护一个等待队列来管理线程的同步状态。它的主要设计思想是,当某个线程尝试获取锁时,如果锁不可用,该线程会被放入等待队列,然后被阻塞。当锁释放时,AQS会从等待队列中唤醒适当的线程,使其能够竞争锁。

AQS什么样

内部类

类名 作用
Node Node类表示等待队列中的一个节点,用于构建等待队列、实现线程的阻塞与唤醒,以及表示等待线程。
ExclusiveNode ExclusiveNodeNode类的子类,表示独占模式(exclusive mode)下的节点,用于独占锁的等待队列。
SharedNode SharedNodeNode类的子类,表示共享模式(shared mode)下的节点,用于共享锁的等待队列。
ConditionNode ConditionNodeNode类的子类,表示条件等待队列中的节点,用于支持条件变量的等待和唤醒机制。
ConditionObject ConditionObjectAbstractQueuedSynchronizer的内部类,用于实现条件变量,允许线程等待特定条件。

这些内部类在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任务。
目录
相关文章
|
17天前
|
安全 Java
利用AQS(AbstractQueuedSynchronizer)实现一个线程同步器
利用AQS(AbstractQueuedSynchronizer)实现一个线程同步器
|
4月前
|
Java
AQS (AbstractQueuedSynchronizer) 概述
AQS (AbstractQueuedSynchronizer) 概述
|
4月前
|
存储 设计模式 安全
理解 AQS 和 ReentrantLock
在多线程编程中,同步机制是确保线程安全的关键。AQS(AbstractQueuedSynchronizer)和ReentrantLock是Java中两种常见的同步机制,它们各自具有不同的特性和适用场景。了解和掌握这两种机制对于编写高效、安全的并发程序至关重要。这篇文章将带你取了解和掌握这两种机制!另外值得一提的是:公平锁的实现与非公平锁是很像的,只不过在获取锁时不会直接尝试使用CAS来获取锁。只有当队列没节点并且state为0时才会去获取锁,不然都会把当前线程放到队列中。
117 1
|
7月前
|
存储 Java 开发者
AbstractQueuedSynchronizer之AQS
AbstractQueuedSynchronizer之AQS
|
11月前
|
Java
16.ReentrantLock全解读
大家好,我是王有志。今天和大家一起聊聊ReentrantLock,它是我们最常见的基于AQS实现的互斥锁。
75 0
|
算法 Java
【JUC基础】05. Synchronized和ReentrantLock
前面两篇中分别讲了Synchronized和ReentrantLock。两种方式都能实现同步锁,且也都能解决多线程的并发问题。那么这两个有什么区别呢? 这个也是一个高频的面经题。
ReentrantLock介绍
ReentrantLock介绍
126 0
Java并发之AbstractQueuedSynchronizer(AQS)详解
Java并发之AbstractQueuedSynchronizer(AQS)详解
Java并发之AbstractQueuedSynchronizer(AQS)详解
|
存储 设计模式 Java
深入理解ReentrantLock
同步锁synchronized和重入锁ReentrantLock都是用于并发程序设计必不可少的手段,在JDK 5.0早期版本中,同步锁性能远远低于重入锁,但是在6.0版本之后,jdk对同步锁做了大量的优化,使得同步锁跟重入锁性能差距并不大,并且jdk团队表示,同步锁还有进一步升级优化的空间
深入理解ReentrantLock
|
调度
ReentrantLock的使用
ReentrantLock的使用
224 0
ReentrantLock的使用