当前位置: 面试刷题>> 你真的理解 AQS 原理了吗?


当然,对于AQS(AbstractQueuedSynchronizer)的理解,作为一名高级程序员,它是Java并发包java.util.concurrent.locks中的核心组件,是实现各种同步器(如ReentrantLock、Semaphore、CountDownLatch等)的基础框架。AQS通过一个int类型的成员变量state来表示同步状态,并通过一个FIFO(先进先出)的线程等待队列来实现线程的阻塞与唤醒,以此管理线程对共享资源的访问。

AQS的核心原理

  1. 状态管理:AQS使用volatile int state来维护同步状态,确保多线程环境下的可见性和有序性。state的值根据不同同步器的实现代表不同的含义,比如在ReentrantLock中,它表示锁被重入的次数。

  2. 节点与队列:AQS内部维护了一个Node节点组成的双向队列(CLH队列),用于存放等待获取锁的线程。每个Node节点包含线程引用、等待状态、前驱节点和后继节点等信息。

  3. 独占与共享:AQS支持两种同步模式——独占模式和共享模式。独占模式下,每次只有一个线程能持有锁;共享模式下,允许多个线程同时获取锁(如Semaphore)。

  4. 加锁与解锁

    • 加锁:当线程尝试获取锁时,如果锁已被其他线程持有,则当前线程会被封装成Node节点并加入到等待队列中,并可能进入阻塞状态。
    • 解锁:持有锁的线程在释放锁时,会唤醒等待队列中的第一个节点所代表的线程,尝试获取锁。

示例代码:基于AQS的简单实现

为了更具体地说明,我们可以简化一个基于AQS的独占锁实现。这里不直接实现完整的ReentrantLock,而是构建一个基本的锁框架。

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class SimpleLock {

    // 自定义同步器
    private static class Sync extends AbstractQueuedSynchronizer {
        // 尝试获取锁
        @Override
        protected boolean tryAcquire(int acquires) {
            // 检查状态是否为0(即锁未被占用)
            if (compareAndSetState(0, 1)) {
                // 设置当前线程为独占线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        // 尝试释放锁
        @Override
        protected boolean tryRelease(int releases) {
            // 释放锁时,状态必须为1
            if (getState() == 1) {
                setExclusiveOwnerThread(null);
                setState(0);
                return true;
            }
            return false;
        }

        // 是否被当前线程持有
        protected boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
    }

    // 同步器实例
    private final Sync sync = new Sync();

    // 加锁
    public void lock() {
        sync.acquire(1);
    }

    // 解锁
    public void unlock() {
        sync.release(1);
    }

    // 尝试非阻塞地获取锁
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    // 其他方法...
}

在上述代码中,SimpleLock通过内部类Sync继承了AbstractQueuedSynchronizer,并实现了必要的tryAcquiretryRelease方法来控制锁的获取与释放。lockunlock方法则分别调用了acquirerelease,它们内部会处理锁的等待队列以及线程的状态转换。

总结

通过理解AQS的原理并编写简单的同步器实现,我们可以看到AQS的强大和灵活性。它不仅是Java并发编程的基石,也为我们理解和设计复杂的并发控制结构提供了有力的支持。在实际开发中,深入掌握AQS对于提升并发程序的设计和实现能力至关重要。在码小课网站上,你可以找到更多关于并发编程和AQS深入应用的文章和教程,帮助你进一步提升技能。

推荐面试题