编程技术分享平台

网站首页 > 技术教程 正文

一文读懂Java并发编程之AbstractQueuedSynchronizer(AQS)

xnh888 2024-10-30 04:42:54 技术教程 25 ℃ 0 评论

AbstractQueuedSynchronizer(AQS)是Java并发编程中一个核心的类,它提供了实现锁、同步器等基础工具的框架,比如ReentrantLock、CountDownLatch、Semaphore等都是基于AQS实现的。在本文中,我们将深入AQS的源码,探究它的实现原理以及其中的关键数据结构和算法。

AQS的设计思路

在介绍AQS的实现原理之前,我们先来看看它的设计思路。AQS的设计思路基于以下两个核心概念:

状态

AQS维护了一个volatile类型的state变量,用于表示同步状态。在不同的同步器中,state的含义不同,比如在ReentrantLock中,它表示锁的持有次数,而在CountDownLatch中,它表示需要等待的线程数。通过修改state变量的值,AQS实现了不同的同步功能。

等待队列

AQS维护了一个FIFO的等待队列,用于保存等待获取同步状态的线程。在AQS中,线程获取同步状态失败时,会被加入到等待队列中,直到获得同步状态或者被中断或者超时才会从等待队列中移除。

通过状态和等待队列,AQS实现了同步器的基础框架。不同的同步器只需要继承AQS并实现特定的方法即可。

AQS的核心数据结构

在AQS中,有几个关键的数据结构,它们是实现AQS同步功能的基础。

Node

Node是AQS中最重要的数据结构之一,它表示等待队列中的一个节点。每个Node节点都有一个waitStatus变量,用于表示节点的状态,比如CANCELLED、SIGNAL、CONDITION等。waitStatus变量的含义在不同的同步器中有不同的解释。

在等待队列中,Node节点通过prev和next两个指针链接在一起,形成一个FIFO的双向链表。通过prev指针,可以找到当前节点的前驱节点,通过next指针,可以找到当前节点的后继节点。

Head和Tail

等待队列中的第一个节点称为Head节点,最后一个节点称为Tail节点。Head和Tail节点并不是通过prev和next指针链接在等待队列中的,而是通过两个volatile类型的变量head和tail记录的。这是为了在高并发场景下,能够快速地对等待队列进行修改。

State

State是AQS中另一个非常重要的数据结构,它是一个volatile类型的变量,用于表示同步状态。State的含义在不同的同步器中有不同的解释。

AQS的核心算法

在AQS中,等待队列的管理是通过一组方法来实现的。其中最重要的方法是acquire()和release()方法。

acquire()方法

acquire()方法用于获取锁,它是AQS中最核心的方法之一。acquire()方法有两个版本,一个是独占式获取锁的版本,一个是共享式获取锁的版本。

独占式获取锁

独占式获取锁是通过acquire()方法的独占式版本来实现的。这个方法有一个参数,表示获取锁的状态值。当一个线程请求锁时,如果锁已经被占用,那么该线程将会被加入到等待队列中,直到锁被释放。

在独占式获取锁的实现中,AQS会首先通过CAS方法来修改状态值。如果修改成功,那么表示当前线程成功获取到了锁。如果修改失败,那么表示当前锁已经被其他线程持有,需要将当前线程加入到等待队列中。

当一个线程成功获取到锁之后,它需要在释放锁之前一直持有锁。如果有其他线程在等待队列中等待锁,那么它们会被唤醒,直到其中一个线程成功获取到锁为止。

共享式获取锁

共享式获取锁是通过acquireShared()方法的共享式版本来实现的。这个方法有一个参数,表示获取锁的状态值。共享式获取锁可以被多个线程同时持有。

在共享式获取锁的实现中,AQS会首先通过CAS方法来修改状态值。如果修改成功,那么表示当前线程成功获取到了锁。如果修改失败,那么表示当前锁已经被其他线程持有,需要将当前线程加入到等待队列中。

当一个线程成功获取到锁之后,它需要在释放锁之前一直持有锁。如果有其他线程在等待队列中等待锁,那么它们会被唤醒,直到所有的等待线程都成功获取到锁为止。

release()方法

release()方法用于释放锁,它是AQS中最核心的方法之一。release()方法有两个版本,一个是独占式释放锁的版本,一个是共享式释放锁的版本。

独占式释放锁

独占式释放锁是通过release()方法的独占式版本来实现的。当一个线程释放锁时,它需要首先释放当前线程持有的锁,然后唤醒等待队列中的下一个线程。如果等待队列中没有其他线程在等待锁,那么释放锁的操作就完成了。

共享式释放锁

共享式释放锁是通过releaseShared()方法的共享式版本来实现的。当一个线程释放锁时,它需要首先释放当前线程持有的锁,然后唤醒等待队列中的下一个线程。如果等待队列中还有其他线程在等待锁,那么继续唤醒下一个线程,直到所有等待线程都成功获取到锁为止。

等待队列的实现

等待队列的实现是AQS中的重点之一。在等待队列中,每个等待线程都会被封装成一个Node节点,节点中保存了等待线程的状态信息和等待时间等数据。等待队列中的节点是一个双向链表,每个节点都有一个前驱节点和一个后继节点。

等待队列中有两种类型的节点,一种是独占节点,另一种是共享节点。独占节点用于独占式获取锁,共享节点用于共享式获取锁。

当一个线程请求锁时,如果锁已经被占用,那么该线程将会被封装成一个节点,并加入到等待队列的末尾。在等待队列中,每个节点的状态有以下几种:

CANCELLED:表示该节点已经被取消,不再参与锁的获取。

SIGNAL:表示该节点需要被唤醒,但是它的前驱节点仍然在等待队列中。

CONDITION:表示该节点在条件队列中等待。

PROPAGATE:表示当前共享式节点需要向后传播唤醒信号。

在等待队列中,每个节点的状态是通过CAS方法来修改的。如果一个线程在等待队列中等待的时间太长,那么它就有可能被中断或者超时,这时候节点的状态会被设置为CANCELLED。

AQS的核心方法

AQS中定义了一系列核心方法,这些方法实现了锁的基本功能,包括获取锁、释放锁、等待锁、唤醒等待线程等操作。其中,acquire()和release()方法是独占式锁的核心方法,acquireShared()和releaseShared()方法是共享式锁的核心方法。

acquire()方法的伪代码如下:

public void acquire(int arg) {
    if (tryAcquire(arg)) {
        return;
    }
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg);
}

acquire()方法首先尝试获取锁,如果成功则直接返回。否则,它将创建一个新的独占式节点,并将其加入到等待队列中。接着,它将调用acquireQueued()方法来使当前线程进入等待状态。

acquireShared()方法的伪代码如下:

public void acquireShared(int arg) {
    if (tryAcquireShared(arg) >= 0) {
        return;
    }
    acquireSharedInterruptibly(arg);
}

acquireShared()方法首先尝试获取共享式锁,如果成功则直接返回。否则,它将调用acquireSharedInterruptibly()方法来使当前线程进入等待状态。

release()方法的伪代码如下:

public boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0) {
            unparkSuccessor(h);
        }
        return true;
    }
    return false;
}

release()方法首先尝试释放锁,如果成功则唤醒下一个等待线程。否则,它将返回false表示释放锁失败。

releaseShared()方法的伪代码如下:

public boolean releaseShared(int arg) {

  if (tryReleaseShared(arg)) {

    doReleaseShared();

    return true;

  }

  return false;

}

releaseShared()方法首先尝试释放共享式锁,如果成功则唤醒下一个等待线程。

应用场景

AQS的应用场景非常广泛,例如Java中的ReentrantLock、Semaphore、CountDownLatch等类都是基于AQS实现的。此外,在并发编程中,AQS也被广泛应用于实现自定义的同步器。

AQS的另一个重要应用场景是线程池。Java中的线程池是通过ThreadPoolExecutor类来实现的,其中线程池的核心机制就是AQS。在线程池中,AQS用于管理线程的状态,控制线程的执行顺序和协调线程之间的同步。

总结

本文对AQS的源码分析进行了深入的讲解,详细介绍了AQS的基本原理、核心方法、等待队列的实现以及应用场景等方面的内容。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表