返回

揭秘AQS原理,手把手带你写ReentrantLock核心实现

Android

AQS:深入理解抽象队列同步器

在并发编程领域,协调多个线程对共享资源的访问至关重要。AbstractQueuedSynchronizer (AQS) 是一种抽象队列同步器,提供了实现各种同步原语的基石。它巧妙地结合了 CLH 队列和 TAS 锁的优点,创造了一种既高效又可靠的同步机制。

CLH 队列:无锁队列

CLH 队列是一种基于 Compare And Swap (CAS) 操作的无锁队列。它允许线程在不阻塞的情况下并发地向队列中添加和删除元素。然而,它也容易出现 ABA 问题,即队列中的一个元素可能被修改多次,但其值始终保持不变。

TAS 锁:自旋锁

TAS 锁(Test And Set Lock)是一种自旋锁,它使用 CAS 操作来获取和释放锁。当一个线程尝试获取锁时,它会不断地检查锁的状态,直到锁处于解锁状态。这种自旋等待的机制虽然简单,但可能会导致处理器资源的浪费。

AQS 的原理

AQS 巧妙地结合了 CLH 队列和 TAS 锁的特性,创建了一种高效且无 ABA 问题的同步机制。它使用一个队列来存储等待获取锁的线程,同时利用 CAS 操作来实现无锁的队列操作。

ReentrantLock:AQS 的具体实现

ReentrantLock 是 Java 并发编程中广泛使用的可重入锁,它基于 AQS 实现。ReentrantLock 的核心数据结构是一个表示锁状态的整型变量。它提供了 lock()、unlock() 和 tryLock() 等方法,允许线程获取、释放和尝试获取锁。

AQS 与 ReentrantLock 的区别

AQS 是一个抽象类,提供各种同步原语的支持,而 ReentrantLock 是 AQS 的具体实现,仅提供可重入锁的支持。此外,AQS 使用队列作为核心数据结构,而 ReentrantLock 使用整型变量。

手把手编写 ReentrantLock

让我们用 Java 手把手编写 ReentrantLock 的核心实现:

public class ReentrantLock {
    private int state;

    public ReentrantLock() {
        state = 0;
    }

    public void lock() {
        while (true) {
            int expected = 0;
            int updated = 1;
            if (compareAndSet(state, expected, updated)) {
                return;
            }
        }
    }

    public void unlock() {
        int expected = 1;
        int updated = 0;
        if (compareAndSet(state, expected, updated)) {
            return;
        }
    }

    public boolean tryLock() {
        int expected = 0;
        int updated = 1;
        return compareAndSet(state, expected, updated);
    }

    private boolean compareAndSet(int expected, int updated) {
        synchronized (this) {
            if (state == expected) {
                state = updated;
                return true;
            }
            return false;
        }
    }
}

常见问题解答

1. AQS 和 ReentrantLock 有什么区别?

AQS 提供了各种同步原语的支持,而 ReentrantLock 仅支持可重入锁。

2. CLH 队列和 TAS 锁有什么优势和劣势?

CLH 队列是无锁的,但容易出现 ABA 问题;TAS 锁简单,但会导致自旋等待。

3. AQS 如何结合 CLH 队列和 TAS 锁?

AQS 使用 CLH 队列存储等待线程,并使用 TAS 锁实现无锁的队列操作。

4. ReentrantLock 如何基于 AQS 实现?

ReentrantLock 将 AQS 的队列替换为一个整型变量,表示锁的状态。

5. 为什么使用 CAS 操作?

CAS 操作提供了一种无锁的方式来更新共享变量,避免了阻塞和竞争条件。

结论

AQS 是并发编程中的一个基础性概念,它提供了高效且可靠的同步机制。ReentrantLock 作为 AQS 的具体实现,广泛用于 Java 并发编程中。通过理解 AQS 的原理和 ReentrantLock 的实现,我们可以更深入地了解并发编程的复杂性和优雅。