网站设计站,无锡免费做网站,六安网站制作费用多少,最新军事新闻最新消息目录 AQS的设计原理1、队列节点 Node 和 FIFO队列结构2、state 的作用3、公平锁与非公平锁 AQS 源码解析1、Node节点2、acquire(int)3、release(int)4、自旋#xff08;Spin#xff09;5、公平性与 FIFO 基于AQS实现的几种同步器1、ReentrantLock#xff1a;可重入独占锁2、… 目录 AQS的设计原理1、队列节点 Node 和 FIFO队列结构2、state 的作用3、公平锁与非公平锁 AQS 源码解析1、Node节点2、acquire(int)3、release(int)4、自旋Spin5、公平性与 FIFO 基于AQS实现的几种同步器1、ReentrantLock可重入独占锁2、ReentrantReadWriteLock可重入读写锁3、Semaphore信号量4、CountDownLatch倒计时门闩 结语 由于AQSAbstractQueuedSynchronizer是Java并发包中的一个关键组件它提供了一种实现同步器如锁和其他同步工具的框架用于实现各种同步器如ReentrantLock、Semaphore等。AQS的核心思想是基于FIFO队列实现阻塞和唤醒线程以及维护同步状态。
AQS的设计原理
AQS 使用一个整数state表示同步状态通过内置的FIFO队列来完成资源获取线程的排队工作。它的主要使用方式是继承子类通过继承AQS并实现它的几个protected方法来管理其状态acquire 和 release并控制线程的排队和阻塞。
1、队列节点 Node 和 FIFO队列结构
AQS 内部通过一个叫做Node的静态内部类来表示队列中的每一个等待线程。而这些节点组成了一个双向链表这就是AQS的等待队列。每一个节点都包含了线程引用、状态和前驱及后继节点的连接。AQS的队列被设计成FIFO确保了首个节点通常是等待时间最长的节点。
2、state 的作用
state是AQS中的核心变量它用来表示同步器的状态。不同的同步器可以使用state来表示不同的意义。例如ReentrantLock用它来表示锁的持有次数Semaphore用它来表示当前可用的许可证数量。
3、公平锁与非公平锁
AQS支持两种锁模式公平锁和非公平锁。公平锁意味着当锁可用时AQS会按照等待时间最长的线程来分配即FIFO而非公平锁则允许新线程插队可能会忽略排队时间较长的线程。非公平锁的吞吐量一般比公平锁要高。
AQS 源码解析
为了深入理解AQS的工作机制我们将分析几个关键的方法。
1、Node节点
Node是AQS内部定义的一个帮助类表示等待队列中的一个节点它的定义如下
static final class Node {volatile int waitStatus;volatile Node prev;volatile Node next;volatile Thread thread;Node nextWaiter;// ...
}waitStatus 表示节点的状态。 prev 指向前一个节点。 next 指向下一个节点。 thread 是执行线程的引用。 nextWaiter 用于构建条件队列。
2、acquire(int)
获取资源的过程它的基本逻辑是如果当前状态允许则尝试获取资源。如果失败则构建节点并进入等待队列可能会循环直到成功获取。
public final void acquire(int arg) {if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}tryAcquire 是由子类实现的方法在锁实现中它尝试直接获取资源。 addWaiter 方法将当前线程封装成节点并加入等待队列。 acquireQueued 方法是使线程在队列中等待必要时阻塞并尝试获取资源。
3、release(int)
当一个线程完成了对资源的使用后它会调用release方法来释放资源。这个方法会尝试设置同步状态并唤醒后续节点。
public final boolean release(int arg) {if (tryRelease(arg)) {Node h head;if (h ! null h.waitStatus ! 0)unparkSuccessor(h);return true;}return false;
}tryRelease 是由子类实现的方法用于释放资源。 unparkSuccessor 用于唤醒等待队列中的后续节点。
4、自旋Spin
在阻塞队列中当一个线程尝试获取但是未能成功时AQS会把这个线程放入队列。在队列中的线程会不断地检查是否能够成为头结点并获取资源这个过程叫做自旋。
5、公平性与 FIFO
公平锁的实现考虑到了队列的FIFO顺序在公平锁中如果队列中有比当前线程更早的线程在等待则当前的线程将加入队列等待。非公平锁允许插队在性能上可能有一定优势但在高并发下可能会导致某些线程饿死。
protected final boolean isHeldExclusively() {// 该方法在子类中实现用来查询当前线程是否独占该锁
}protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();
}protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();
}protected boolean tryReleaseShared(int arg) {throw new UnsupportedOperationException();
}以上是用于扩展的几个方法。对于独占模式需要实现tryAcquire和tryRelease方法对于共享模式则需要实现tryAcquireShared和tryReleaseShared。
基于AQS实现的几种同步器
1、ReentrantLock可重入独占锁
ReentrantLock 是一种可重入的互斥独占锁它允许已经获取到锁的线程再次获取锁而不会被阻塞。这种锁的实现基于AQS。
这里是一个精简版本的ReentrantLock获取锁(lock)和释放锁(unlock)的基本原理
public class ReentrantLock {// 内部持有AQS的一个子类private final Sync sync;// Sync是AQS的一个子类abstract static class Sync extends AbstractQueuedSynchronizer {// 获取锁 abstract void lock();// 尝试释放锁protected final boolean tryRelease(int releases) {int c getState() - releases;if (Thread.currentThread() ! getExclusiveOwnerThread()) {throw new IllegalMonitorStateException();}boolean free false;if (c 0) {free true;setExclusiveOwnerThread(null);}setState(c);return free;}}// ... 其他方法省略
}2、ReentrantReadWriteLock可重入读写锁
ReentrantReadWriteLock允许多个线程同时读取资源但只允许一个线程写入资源。适用于多读少写场景。它也是基于AQS实现的。
其内部主要有两个类ReadLock和WriteLock这两个类都是通过控制AQS中的状态来实现锁的功能
public class ReentrantReadWriteLock {private final ReentrantReadWriteLock.ReadLock readerLock;private final ReentrantReadWriteLock.WriteLock writerLock;// AQS的子类实现private final Sync sync;static final class Sync extends AbstractQueuedSynchronizer {// 写锁的获取和释放// ...// 读锁的获取和释放// ...}public class ReadLock {// 获取读锁public void lock() {sync.acquireShared(1);}// 释放读锁public void unlock() {sync.releaseShared(1);}}public class WriteLock {// 获取写锁public void lock() {sync.acquire(1);}// 释放写锁public void unlock() {sync.release(1);}}
}3、Semaphore信号量
Semaphore 信号量用于同时多个线程访问场景可以初始化时设置同时访问线程数。其核心方法是acquire()和release()它们分别用于获取和释放许可。
public class Semaphore {private final Sync sync;// AQS的子类实现static abstract class Sync extends AbstractQueuedSynchronizer {// 获取许可abstract void acquireShared(int permits);// 释放许可final boolean releaseShared(int permits) {// ...}}public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public void release() {sync.releaseShared(1);}
}4、CountDownLatch倒计时门闩
CountDownLatch是一个同步辅助类在完成一组正在其他线程中执行的操作之前它允许一个或多个线程一直等待。
public class CountDownLatch {private final Sync sync;// AQS的子类实现private static final class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count);}// 减少锁存器计数public void countDown() {releaseShared(1);}// 等待直到锁存器计数为零public void await() throws InterruptedException {acquireSharedInterruptibly(1);}}public CountDownLatch(int count) {this.sync new Sync(count);}public void countDown() {sync.countDown();}public void await() throws InterruptedException {sync.await();}
}以上代码只是为了解释概念并未展示真正的实现细节。在实际的JDK实现中这些类的内部结构更为复杂包括了性能优化、状态管理、线程调度和锁升级等机制。您需要直接查看JDK源码以获取完整和准确的实现。
结语
AQS是Java并发编程中的重石它通过内置的FIFO队列以及对共享资源状态的管理极大地简化了同步器的实现。通过继承AQS并实现其方法我们可以创造出性能卓越和功能强大的同步器。当理解了其背后的原理和源码后我们就能更好地利用这一强大工具来构建可靠的并发应用。