AQS

Java AQS(AbstractQueuedSynchronizer)是Java并发包(java.util.concurrent)中的一个基础同步框架。它为实现锁和其他同步器(如Semaphore、CountDownLatch、ReentrantLock等)提供了一个通用的底层机制。

核心特点:

实现自定义锁或同步器时,继承AQS并实现tryAcquire、tryRelease等方法。 AQS极大地简化了Java并发工具的开发,是Java并发包的核心基础

数据结构

abstract static class Node {
        volatile Node prev;       // initially attached via casTail
        volatile Node next;       // visibly nonnull when signallable
        Thread waiter;            // visibly nonnull when enqueued
        volatile int status;      // written by owner, atomic bit ops by others
}

CLH队列(Craig, Landin, and Hagersten队列)是一种基于链表的自旋锁队列,AQS的同步队列就是基于CLH队列实现的。

特点:

CLH队列是一个虚链表,节点代表等待获取锁的线程。 每个线程在本地保存一个前驱节点,通过前驱节点的状态判断自己是否可以获取锁。 线程入队时会自旋等待前驱节点释放锁,减少了竞争和CPU资源浪费。 在AQS中的应用:

AQS通过CLH队列实现线程的有序排队和唤醒。 线程获取锁失败时,会被包装成一个Node节点加入队列尾部。 只有队列头节点的下一个节点才有资格被唤醒尝试获取锁,保证了公平性。 CLH队列的设计让AQS能够高效地管理大量线程的同步等待。

头节点唤醒

在AQS中,等待队列的头节点(head)用于管理线程的同步和唤醒。其主要机制如下:

等待: 当线程获取锁失败时,会被封装成一个节点加入到AQS的同步队列(一个双向链表),并阻塞(通常通过LockSupport.park()实现)。只有队列头节点前面的线程被唤醒后,当前节点才有机会获取锁。

唤醒: 当锁被释放时,AQS会调用unparkSuccessor(head)方法唤醒队列中的下一个节点(即头节点的下一个节点)。具体做法是通过LockSupport.unpark()唤醒等待线程,使其重新尝试获取锁。

总结:

头节点本身不代表实际线程,只是一个占位节点。 唤醒操作总是针对头节点的下一个节点(即等待时间最长的线程)。 通过这种方式,实现了线程的有序等待和唤醒,保证了同步的公平性和效率。