AbstractQueuedSynchronizer 为 java.util.concurrent.locks 包下的一个抽象类,简称 AQS(抽象队列同步器)。
并发包(JUC)中的 ReentrantLock、Semaphore、ReentrantReadWriteLock、SynchronousQueue、FutureTask 等,底层都是基于 AQS 来实现的。
一、使用
1.AQS 采用了模板模式
自定义同步器时需要重写下面几个方法。
// 该线程是否正在独占资源(是否在独占模式下被线程占用)。只有用到 condition 才需要去实现它。 boolean isHeldExclusively(); // 独占方式。尝试获取资源,成功则返回 true,失败则返回 false。 boolean tryAcquire(int arg); // 独占方式。尝试释放资源,成功则返回 true,失败则返回 false。 // 成功后,等待中的其他线程此时将有机会获取到资源。 boolean tryRelease(int arg); // 共享方式。尝试获取资源。负数表示失败;0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。 int tryAcquireShared(int arg); // 共享方式。尝试释放资源,成功则返回 true,失败则返回 false。 boolean tryReleaseShared(int);
默认每个方法都抛出 UnsupportedOperationException。 这些方法的实现必须是内部线程安全的,并且通常应该简短而不是阻塞。
AQS 类中的其它方法都是 final 修饰,无法被其它类使用。
2.AQS 对资源的共享方式
一般自定义同步器,要么是独占(tryAcquire-tryRelease)方式,要么是共享(tryAcquireShared-tryReleaseShared)方式。(AQS 也支持同时实现,如 ReentrantReadWriteLock)
-
Exclusive(独占):只有一个线程能执行,如 ReentrantLock。独占又可分为公平锁和非公平锁:
-
公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
-
非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
-
Share(共享):多个线程可同时执行,如 Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock。
3.自定义同步器(独占式)
import java.util.concurrent.locks.AbstractQueuedSynchronizer; public class MyMutex { private final Sync sync = new Sync(); // 当前状态为 0 时获取锁,然后进行 CAS 设置同步状态 // 未获取到当前线程则会进入同步队列等待 public void lock() { sync.acquire(1); } // 释放锁,将状态设置为 0 public void unlock() { sync.release(1); } // 是否处于被当前线程占有状态 public boolean isLocked() { return sync.isHeldExclusively(); } private static final class Sync extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire(int arg) { // 首先判断状态是否=0,如果状态=0,就将 status 设置为 1 if (compareAndSetState(0, 1)) { // 将当前线程赋值给独占模式的 onwer setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } @Override protected boolean tryRelease(int arg) { // 判断当前是获得资源的线程 if (Thread.currentThread() != getExclusiveOwnerThread() || getState() == 0) { throw new IllegalMonitorStateException(); } // 没有线程拥有这个锁 setExclusiveOwnerThread(null); setState(0); return true; } @Override protected boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread() && getState() == 1; } } }