AQS的介绍

130 阅读4分钟

AQS的介绍

AQS(AbstractQueuedSynchronizer)是一个用于实现锁和同步器的Java类,是Java并发包下面的一个基础组件。Lock、Semaphore、CountDownLatch等就是基于AQS实现的。

AQS使用一个先进先出(FIFO)的队列来管理线程的阻塞和唤醒操作。每个线程在获取锁时,如果锁已经被其他线程占用,就会被阻塞并加入到同步队列中。当锁被释放时,AQS会从同步队列中唤醒一个线程。

AQS类的核心思想就是使用状态表示同步状态,通过CAS操作来保证状态的正确性,使用同步队列来维护等待线程集合。在使用AQS实现同步器时,一般需要扩展AQS类,并实现其中的tryAcquire、tryRelease等方法,以实现特定的同步策略。

下面是一个简单的自定义同步器的示例:

public class MyLock {
    private final Sync sync = new Sync();

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

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

    private static class Sync extends AbstractQueuedSynchronizer {
        protected boolean tryAcquire(int acquires) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int releases) {
            if (getState() == 0)
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }
}

在这个示例中,我们自定义了一个简单的锁MyLock,其中使用了AQS类的Sync作为同步器。我们实现了Sync中的tryAcquire、tryRelease、isHeldExclusively三个方法,以实现基于AQS的同步策略。MyLock的lock和unlock方法则是对Sync的封装,供用户使用。

总之,AQS是Java并发包中一个比较核心的组件,通过使用AQS实现自定义的同步策略,可以更好地掌握Java并发编程的基础知识。

AQS在项目中的使用场景

AQS在Java并发编程中被广泛应用,主要用于构建高效、可扩展的同步组件。以下是AQS在项目中的一些常见应用场景:

  1. ReentrantLock:ReentrantLock是一个可重入的互斥锁,它的实现就是基于AQS。ReentrantLock的实例持有一个AQS的子类FairSync或NonfairSync,通过调用Sync中的tryAcquire和tryRelease方法来实现对锁的获取和释放。
  2. Semaphore:Semaphore是一个计数器,用于管理同时访问某个资源的线程数。Semaphore可以用于限制并发线程的数量,也可以用于实现资源池等场景。Semaphore中的信号量计数器也是由AQS进行管理的。
  3. CountDownLatch:CountDownLatch是一个同步工具类,用于控制多个线程之间的执行顺序。CountDownLatch内部维护了一个AQS同步器,所有线程在调用await方法时会被阻塞,直到计数器达到0或超时返回。
  4. Condition:Condition是Java并发包中的一个条件变量,用于在等待某个条件成立时阻塞线程。Condition的实现也是基于AQS的。

举例说明

  1. ReentrantLock

在Java并发编程中,ReentrantLock常用于实现线程间的互斥访问。例如,在某个高并发场景下,需要确保对某个资源的访问是原子性的,这时可以使用ReentrantLock来实现。示例代码如下:

private final ReentrantLock lock = new ReentrantLock();

public void accessResource() {
    lock.lock();
    try {
        //访问资源
    } finally {
        lock.unlock();
    }
}
  1. Semaphore

在某些应用场景下,需要限制系统中的资源访问线程数量,例如连接池的连接数量、线程池中的线程数量等。这时可以使用Semaphore来实现。示例代码如下:

private final Semaphore sem = new Semaphore(10);

public void accessResource() throws InterruptedException {
    sem.acquire();
    try {
        //访问资源
    } finally {
        sem.release();
    }
}

上述代码中,线程在访问资源之前,需要先通过semaphore对象的acquire方法获取许可证,当许可证数量为0时,线程会被阻塞,直到有其他线程释放许可证为止。

  1. Condition

在某些场景下,需要在线程间传递信号,例如某个线程需要等待某个条件成立后再继续执行。这时可以使用Condition来实现。示例代码如下:

private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

public void waitCondition() throws InterruptedException {
    lock.lock();
    try {
        while (!conditionIsMet()) {
            condition.await();
        }
        //执行业务操作
    } finally {
        lock.unlock();
    }
}

public void signalCondition() {
    lock.lock();
    try {
        //更新条件状态
        condition.signalAll();
    } finally {
        lock.unlock();
    }
}

在上述代码中,线程在等待条件成立时,调用condition对象的await方法,线程会被阻塞,直到有其他线程调用signal方法唤醒它。信号的发送者需要先获取锁,并在更新条件状态后,调用signalAll方法来通知所有等待线程