AQS极简版

1. 先写 Node 节点(等待队列节点)

java 复制代码
import java.util.concurrent.locks.LockSupport;

// 等待队列节点
class Node {
    // 前驱节点
    Node prev;
    // 后继节点
    Node next;
    // 排队的线程
    Thread thread;

    public Node(Thread thread) {
        this.thread = thread;
    }
}

2. 手写极简 AQS(核心)

java 复制代码
abstract class SimpleAQS {
    // 锁状态:0=无锁,1=已加锁,>1 重入
    private volatile int state;

    // 队列头
    private volatile Node head;
    // 队列尾
    private volatile Node tail;

    // 持有锁的线程
    private Thread exclusiveOwnerThread;

    // ========== 原子操作 state ==========
    protected final boolean compareAndSetState(int expect, int update) {
        // 真实 JDK 用 unsafe CAS,这里简化理解
        if (state == expect) {
            state = update;
            return true;
        }
        return false;
    }

    // ========== 入队逻辑(尾插) ==========
    private Node enqueue() {
        Node node = new Node(Thread.currentThread());
        // 尾插
        Node t = tail;
        if (t == null) {
            head = node;
            tail = node;
        } else {
            node.prev = t;
            tail = node;
            t.next = node;
        }
        return node;
    }

    // ========== 阻塞等待 ==========
    private void park(Node node) {
        for (;;) {
            Node p = node.prev;

            // 如果前驱是头节点,再尝试抢一次锁
            if (p == head && tryAcquire(1)) {
                // 抢到了,把自己设为新 head
                head = node;
                node.prev = null;
                node.thread = null;
                return;
            }

            // 抢不到,阻塞线程
            LockSupport.park(this);

            // 被唤醒后继续循环抢锁
        }
    }

    // ========== 解锁唤醒下一个 ==========
    private void unparkSuccessor() {
        Node h = head;
        if (h != null && h.next != null) {
            // 唤醒队首第一个等待线程
            LockSupport.unpark(h.next.thread);
        }
    }

    // ========== 模板方法(子类实现) ==========
    protected abstract boolean tryAcquire(int arg);
    protected abstract boolean tryRelease(int arg);

    // ========== 对外加锁 ==========
    public final void acquire(int arg) {
        // 尝试加锁成功直接返回
        if (!tryAcquire(arg)) {
            // 失败 → 入队 + 阻塞
            Node node = enqueue();
            park(node);
        }
    }

    // ========== 对外解锁 ==========
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            // 唤醒后继
            unparkSuccessor();
            return true;
        }
        return false;
    }

    // ========== getter/setter ==========
    public int getState() { return state; }
    public void setState(int state) { this.state = state; }
    public Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; }
    public void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }
}

3. 基于手写 AQS 实现 ReentrantLock

java 复制代码
import java.util.concurrent.locks.Lock;
import java.util.Collection;

public class SimpleReentrantLock extends SimpleAQS implements Lock {

    // ========== 加锁实现(非公平) ==========
    @Override
    protected boolean tryAcquire(int arg) {
        Thread current = Thread.currentThread();
        int c = getState();

        // 1. 无锁,CAS 抢锁
        if (c == 0) {
            if (compareAndSetState(0, arg)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 2. 可重入:当前线程已经持有锁
        else if (current == getExclusiveOwnerThread()) {
            setState(c + arg);
            return true;
        }
        // 3. 抢锁失败
        return false;
    }

    // ========== 解锁实现 ==========
    @Override
    protected boolean tryRelease(int arg) {
        int c = getState() - arg;
        // 只有持有锁线程才能释放
        if (Thread.currentThread() != getExclusiveOwnerThread()) {
            throw new IllegalMonitorStateException();
        }

        boolean free = false;
        // state 减到 0,才算完全释放
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

    // ========== Lock 接口实现 ==========
    @Override
    public void lock() {
        acquire(1);
    }

    @Override
    public void unlock() {
        release(1);
    }

    // 下面这些方法简单实现,方便运行
    @Override public void lockInterruptibly() {}
    @Override public boolean tryLock() { return false; }
    @Override public boolean tryLock(long time, java.util.concurrent.TimeUnit unit) { return false; }
    @Override public java.util.concurrent.locks.Condition newCondition() { return null; }
}

4. 测试多线程安全(可直接运行)

java 复制代码
public class TestLock {
    static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        SimpleReentrantLock lock = new SimpleReentrantLock();

        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                lock.lock();
                try {
                    count++;
                } finally {
                    lock.unlock();
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("count = " + count);
        // 结果一定是 20000,线程安全
    }
}

运行结果:

复制代码
count = 20000

5. 极简总结(对应你手写的 AQS)

  1. AQS = state + 队列 + CAS
  2. lock()tryAcquire
    • 抢锁成功 → 直接执行
    • 抢锁失败 → 入队 → park 阻塞
  3. unlock()tryRelease
    • state - 1
    • state == 0 → 完全释放 → 唤醒队列第一个节点
  4. 可重入:同一个线程多次加锁 → state 累加
  5. 队列是双向链表,保证公平排队

6.AQS的核心思想

当前一个节点已经在临界区上运行时,就将它的下一个节点唤醒,其他节点还处于休眠状态,让它的下一个节点不断轮询,一旦当前节点释放,下一个节点就很容易抢到,不会与其他线程竞争。核心思想就是:

  1. 只有队列的第二个节点(head.next)会被唤醒

  2. 其他后面的节点全部在睡觉,不参与竞争

  3. 被唤醒的节点,会在循环里尝试抢锁

  4. 因为没人跟它抢,所以一抢一个准

  5. 抢到后,它变成新的 head,然后继续唤醒下一个

这就是 AQS 大名鼎鼎的:链式唤醒(唤醒一个,只唤醒队首第二个)、无竞争抢锁(只有一个线程抢锁)。

park() 只有队列第二个线程会抢锁其他线程都在睡觉,不竞争

unparkSuccessor() 只唤醒队列第二个线程不唤醒全部,不产生竞争

相关推荐
zhangchaoxies2 小时前
JavaScript中Tree-shaking失效的场景及其优化对策
jvm·数据库·python
2501_914245932 小时前
SQL在GROUP BY中如何保留非聚合列_配合ANY_VALUE或窗口函数
jvm·数据库·python
weixin_580614002 小时前
如何防止SQL注入篡改数据_实施双重身份验证与授权
jvm·数据库·python
2401_897190552 小时前
SQL视图占空间吗_理解视图定义与存储机制的底层逻辑
jvm·数据库·python
qq_424098562 小时前
C#怎么实现UDP广播通信_C#如何搭建Socket网络【核心】
jvm·数据库·python
2501_914245932 小时前
Python Web开发如何防范SQL注入_使用参数化查询与ORM实践
jvm·数据库·python
yejqvow122 小时前
Golang怎么做模糊测试fuzz_Golang Fuzz测试教程【高效】
jvm·数据库·python
2401_897190552 小时前
mysql如何通过mysqldump备份视图与触发器_使用相关参数
jvm·数据库·python
a9511416422 小时前
如何编写带默认值的SQL存储过程_简化前端调用接口设计
jvm·数据库·python