java.util.concurrent 同步类器中的AQS

AQS框架与Java并发工具
本文深入解析了java.util.concurrent包中基于AQS(AbstractQueuedSynchronizer)框架的并发工具,包括ReentrantLock、Semaphore、CountDownLatch等。探讨了AQS如何简化锁和同步器的实现,以及这些工具在不同场景下的应用。

在 java.util.concurrent 中包含许多的阻塞类:

  • ReentrantLock
  • Semaphore
  • ReentrantReadWriteLock
  • CountDownLatch
  • SynchronousQueue
  • FutureTask

它们都是基于 AQS 构建的。
java8 api 文档: https://docs.oracle.com/javase/8/docs/api/
中文:http://www.matools.com/api/java8

AbstractQueuedSynchronizer(AQS)

AQS 是一个构建锁和同步器的框架。
AQS 解决了在实现同步器时涉及的大量的细节问题,比如等待线程使用 FIFO 队列操作顺序。在不同的同步器中还可以定义一些灵活的标准来判断某个线程是通过还是等待。

 /**
* Returns the current value of synchronization state.
 * This operation has memory semantics of a {@code volatile} read.
 * @return current state value
 */
protected final int getState() {
    return state;
}

/**
 * Sets the value of synchronization state.
 * This operation has memory semantics of a {@code volatile} write.
 * @param newState the new state value
 */
protected final void setState(int newState) {
    state = newState;
}

/**
 * Atomically sets synchronization state to the given updated
 * value if the current state value equals the expected value.
 * This operation has memory semantics of a {@code volatile} read
 * and write.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that the actual
 *         value was not equal to the expected value.
 */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

例如当某些子类实现 tryAcquireShared ,返回一个负值,表示获取操作失败;
当返回是 0 时,表示同步器通过独占方式获取;
返回正值,表示同步器通过非独占方式被获取。

ReentrantLock

ReentrantLock 只支持独占的方式获取,因此其实现了 tryAcquire、tryRelease 和 isHeldExclusively

ReentrantLock 将同步的状态用于保存锁获取操作的次数,并且维护一个 owner 变量来保存当前所有者线程的标识符,只有在当前线程刚刚获取到锁,或者正要释放锁的时候,才会修改这个变量。
在 tryRealse 中检查 owner 域,确保当前线程在执行 unlock 操作之前已经获取了锁。

例如 tryAcquire

/**
* Fair version of tryAcquire.  Don't grant access unless
 * recursive call or no waiters or is first.
 */
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

当一个线程尝试获取锁时, tryAcquire 将首先检查锁的状态。如果锁未被持有,那么将尝试更新锁的状态表示锁已经被持有了。由于状态可能在检查后被修改,所以使用 compareAndSetState 来原子更新。
如果锁状态表示它已经被持有,并且当前线程是持有者,获取计数递增,如果当前线程不是锁的拥有者,获取锁失败。

Semaphore 和 CountDownLatch

Semaphore 将 AQS 的同步状态用于保存当前可用许可的数量。 首先计算剩余许可的数量,如果没有足够的许可,返回一个值表示获取失败。如果还有剩余的许可,那么 nonfairTryAcquireShared 通过 compareAndSetState 方法降低许可的计数。
如果获取成功,返回一个数表示获取操作成功。

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

CountDownLatch 使用 AQS 的方式和 Semaphore 类似。

`java.util.concurrent.locks.ReentrantLock` 是 Java 并发编程中一个重要的工具,属于 `java.util.concurrent.locks` 包,提供了比内置 `synchronized` 关键字更为灵活的锁机制,用于多线程环境下的同步控制[^1]。 ### 特点 - **可重入性**:同一个线程可以多次获取同一把锁,而不会出现死锁的情况。在获取锁时,锁的持有计数会增加;释放锁时,持有计数会减少,直到计数为 0 时,锁才被完全释放[^3]。 - **公平性选择**:支持公平锁和非公平锁。公平锁会按照线程请求锁的顺序依次获取锁;非公平锁则允许线程在锁被释放时直接尝试获取锁,而不考虑等待顺序。默认情况下,`ReentrantLock` 是非公平锁。 - **可中断性**:线程在等待获取锁的过程中,可以被中断,避免无限期等待。 - **条件变量支持**:可以通过 `newCondition()` 方法创建多个条件变量,实现更精细的线程间协作。 ### 使用方法 ```java import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); public void performTask() { lock.lock(); try { // 执行需要同步的操作 System.out.println("Task is being executed."); } finally { lock.unlock(); } } public static void main(String[] args) { ReentrantLockExample example = new ReentrantLockExample(); example.performTask(); } } ``` 在上述代码中,`lock.lock()` 方法用于获取锁,`lock.unlock()` 方法用于释放锁。为了确保锁最终能被释放,通常将释放锁的操作放在 `finally` 块中。 ### 原理 `ReentrantLock` 是基于 AQS(AbstractQueuedSynchronizer)实现的。AQS 使用一个整型的 `volatile` 变量(命名为 `state`)来维护同步状态,这是 `ReentrantLock` 内存语义实现的关键。在 `ReentrantLock` 中,不管是公平锁还是非公平锁,当锁的状态 `c` 为 0 时,表示锁未被持有,尝试通过 `compareAndSetState` 方法来获取锁。如果当前线程已经持有了锁,再次获取锁时,会将锁的状态值 `c` 增加,体现了可重入性。当释放锁时,会将状态值 `c` 减少,直到为 0 时表示锁被完全释放[^4]。 ### 应用场景 `ReentrantLock` 提供了一种灵活且功能强大的线程同步机制,尤其适用于复杂的并发场景。相比于 `synchronized`,`ReentrantLock` 允许更细粒度的控制,并支持公平锁、可中断锁和可重入锁等高级特性。在业务应用中,`ReentrantLock` 可以确保高并发情况下共享资源的正确性,适用于电商、库存管理等系统[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值