什么是Java的AbstractQueuedSynchronizer (AQS)

在 Java 并发编程中,设计出一种高效、通用且可扩展的同步机制一直是一个重要的课题。AbstractQueuedSynchronizer(AQS)的出现,正是为了满足这一需求。本文将深入探讨 AQS 设计的动机及其意义,并通过源码解析详细解释其实现过程,最后结合实际应用场景展示 AQS 的强大功能。

高效的同步机制

在多线程环境下,线程需要协同工作,避免资源竞争和死锁问题。然而,线程的管理和调度是一个复杂且开销较大的任务。高效的同步机制可以极大地减少线程切换和上下文切换的开销,从而提升系统的性能。AQS 通过引入 CLH 队列(Craig, Landin, and Hagersten 队列),有效地解决了线程的排队和调度问题。

java

// CLH 队列的基本结构
static final class Node {
    static final Node SIGNAL = new Node(); // 用于标记节点需要唤醒
    static final Node CANCELLED = new Node(); // 用于标记节点已取消

    volatile Node prev; // 前驱节点
    volatile Node next; // 后继节点
    volatile Thread thread; // 节点对应的线程
    volatile int waitStatus; // 节点等待状态

    // 默认构造函数,用于初始化head或SHARED标记
    Node() { 
    }

    // 用于addWaiter方法
    Node(Thread thread, Node mode) { 
        this.next = null;
        this.thread = thread;
        this.waitStatus = 0;
    }
}
通用的同步机制

在 Java 并发包中,有多种不同类型的锁和同步器,如 ReentrantLockSemaphoreCountDownLatch 等。虽然它们的功能和使用场景各不相同,但其底层实现逻辑具有相似性。AQS 提供了一套通用的模板方法,使得这些锁和同步器可以基于同一套机制进行实现,从而减少了代码冗余和维护成本。

java

// 以ReentrantLock为例,它通过扩展AQS来实现锁的功能
public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    // 内部抽象类,扩展AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();

        // 尝试非公平地获取锁
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (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;
        }
    }

    // 非公平锁实现
    static final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    public ReentrantLock() {
        sync = new NonfairSync();
    }

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

    public void unlock() {
        sync.release(1);
    }
}
可扩展性

AQS 提供了一套可扩展的基础设施,允许开发者根据具体需求自定义同步器。通过继承 AQS 并重写其模板方法,开发者可以轻松实现自己的同步器。AQS 的设计使得这些自定义同步器能够在高并发环境下保持高效和稳定。

java

// 自定义同步器的示例
class CustomSync extends AbstractQueuedSynchronizer {
    // 尝试获取独占锁
    @Override
    protected boolean tryAcquire(int arg) {
        // 自定义获取锁的逻辑
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    // 尝试释放独占锁
    @Override
    protected boolean tryRelease(int arg) {
        // 自定义释放锁的逻辑
        if (getState() == 0) {
            throw new IllegalMonitorStateException();
        }
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
}

// 使用自定义同步器
public class CustomLock {
    private final CustomSync sync = new CustomSync();

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

    public void unlock() {
        sync.release(1);
    }
}
提高代码可读性和可维护性

通过 AQS 提供的通用模板方法,锁和同步器的实现逻辑变得更加清晰和可维护。开发者只需关注具体同步器的核心逻辑,而不必重复实现线程管理和调度的细节。这大大提高了代码的可读性和可维护性。

保证线程安全

AQS 使用 volatile 关键字和 CAS(Compare-And-Swap)操作来保证同步状态的线程安全。这些机制确保了在高并发环境下,线程对共享资源的操作是安全的。

java

// 共享变量,使用volatile修饰保证线程可见性
private volatile int state;

// 原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

应用场景

我们已经了解了 AQS 的设计动机和基本结构,接下来我们将探讨它在实际应用中的一些常见场景。

1. 实现互斥锁 (ReentrantLock)

互斥锁是最常见的锁类型,用于确保在同一时刻只有一个线程可以访问共享资源。ReentrantLock 是 Java 提供的一个可重入锁,它基于 AQS 实现。

java

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();

        // 尝试非公平地获取锁
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (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;
        }
    }

    static final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    public ReentrantLock() {
        sync = new NonfairSync();
    }

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

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

在 ReentrantLock 中,通过扩展 AQS 并重写其模板方法 tryAcquire 和 tryRelease,实现了互斥锁的功能。ReentrantLock 提供了可重入性,即同一线程可以多次获取锁,并在释放时相应地减少持有次数。

2. 实现读写锁 (ReentrantReadWriteLock)

读写锁允许多个线程同时读取共享资源,但在写操作时需要独占锁。ReentrantReadWriteLock 提供了一对读锁和写锁,它们基于 AQS 实现。

java

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    private final ReadLock readerLock;
    private final WriteLock writerLock;
    final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        // 获取读锁
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        // 获取写锁
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

        abstract boolean readerShouldBlock();
        abstract boolean writerShouldBlock();

        protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

        protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                if (w == 0 ||
                    current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
    }

    public ReentrantReadWriteLock() {
        sync = new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
}

在 ReentrantReadWriteLock 中,通过扩展 AQS 并重写其模板方法 tryAcquire 和 tryRelease,实现了读写锁的功能。读锁和写锁分别用于控制读操作和写操作的并发访问。

3. 实现计数信号量 (Semaphore)

计数信号量用于控制对有限资源的访问。Semaphore 可以限制同时访问资源的最大线程数,它基于 AQS 实现。

java

public class Semaphore implements java.io.Serializable {
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        Sync(int permits) {
            setState(permits);
        }

        final int getPermits() {
            return getState();
        }

        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) // 溢出
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
    }

    static final class NonfairSync extends Sync {
        NonfairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

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

在 Semaphore 中,通过扩展 AQS 并重写其模板方法 tryAcquireShared 和 tryReleaseShared,实现了信号量的功能。计数信号量用于限制同时访问资源的线程数。

4. 实现倒计时门闩 (CountDownLatch)

倒计时门闩用于等待一组线程完成各自的任务。CountDownLatch 基于 AQS 实现,通过一个计数器来控制线程的等待和释放。

java

public class CountDownLatch {
    private final Sync sync;

    private static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c - 1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public void countDown() {
        sync.releaseShared(1);
    }

    public long getCount() {
        return sync.getCount();
    }
}

在 CountDownLatch 中,通过扩展 AQS 并重写其模板方法 tryAcquireShared 和 tryReleaseShared,实现了倒计时门闩的功能。倒计时门闩用于等待一组线程完成各自的任务。

结论

AQS 为 Java 并发包中的各种锁和同步器提供了一个通用的基础设施。通过扩展 AQS 并重写其模板方法,开发者可以轻松实现互斥锁、读写锁、信号量、倒计时门闩等不同类型的同步器,以满足不同的并发需求。AQS 的设计不仅提高了代码的可读性和可维护性,还保证了高并发环境下的线程安全。

  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值