Java基础知识_AQS
一、AQS是什么?
首先我们来普及一下juc是什么:juc其实就是包的缩写java.util.concurrent
不要被人家唬到了,以为juc是什么牛逼玩意儿,实际上就是包而已
我们可以发现lock包下有三个抽象类:

AbstractOwnableSynchronizer
AbstractQueuedLongSynchronizer
AbstractQueuedSynchronizer
通常地:AbstractQueuedSynchronizer简称为AQS
我们Lock之类的常见的两个锁都是基于他实现的

那么我们来看看AbstractQueuedSynchronizer到底是什么,看一个类最快的途径就是看他的顶部注释
1 /** 2 * Provides a framework for implementing blocking locks and related 3 * synchronizers (semaphores, events, etc) that rely on 4 * first-in-first-out (FIFO) wait queues. This class is designed to 5 * be a useful basis for most kinds of synchronizers that rely on a 6 * single atomic {@code int} value to represent state. Subclasses 7 * must define the protected methods that change this state, and which 8 * define what that state means in terms of this object being acquired 9 * or released. Given these, the other methods in this class carry 10 * out all queuing and blocking mechanics. Subclasses can maintain 11 * other state fields, but only the atomically updated {@code int} 12 * value manipulated using methods {@link #getState}, {@link 13 * #setState} and {@link #compareAndSetState} is tracked with respect 14 * to synchronization. 15 * 16 * <p>Subclasses should be defined as non-public internal helper 17 * classes that are used to implement the synchronization properties 18 * of their enclosing class. Class 19 * {@code AbstractQueuedSynchronizer} does not implement any 20 * synchronization interface. Instead it defines methods such as 21 * {@link #acquireInterruptibly} that can be invoked as 22 * appropriate by concrete locks and related synchronizers to 23 * implement their public methods. 24 * 25 * <p>This class supports either or both a default <em>exclusive</em> 26 * mode and a <em>shared</em> mode. When acquired in exclusive mode, 27 * attempted acquires by other threads cannot succeed. Shared mode 28 * acquires by multiple threads may (but need not) succeed. This class 29 * does not "understand" these differences except in the 30 * mechanical sense that when a shared mode acquire succeeds, the next 31 * waiting thread (if one exists) must also determine whether it can 32 * acquire as well. Threads waiting in the different modes share the 33 * same FIFO queue. Usually, implementation subclasses support only 34 * one of these modes, but both can come into play for example in a 35 * {@link ReadWriteLock}. Subclasses that support only exclusive or 36 * only shared modes need not define the methods supporting the unused mode. 37 * 38 * <p>This class defines a nested {@link ConditionObject} class that 39 * can be used as a {@link Condition} implementation by subclasses 40 * supporting exclusive mode for which method {@link 41 * #isHeldExclusively} reports whether synchronization is exclusively 42 * held with respect to the current thread, method {@link #release} 43 * invoked with the current {@link #getState} value fully releases 44 * this object, and {@link #acquire}, given this saved state value, 45 * eventually restores this object to its previous acquired state. No 46 * {@code AbstractQueuedSynchronizer} method otherwise creates such a 47 * condition, so if this constraint cannot be met, do not use it. The 48 * behavior of {@link ConditionObject} depends of course on the 49 * semantics of its synchronizer implementation. 50 * 51 * <p>This class provides inspection, instrumentation, and monitoring 52 * methods for the internal queue, as well as similar methods for 53 * condition objects. These can be exported as desired into classes 54 * using an {@code AbstractQueuedSynchronizer} for their 55 * synchronization mechanics. 56 * 57 * <p>Serialization of this class stores only the underlying atomic 58 * integer maintaining state, so deserialized objects have empty 59 * thread queues. Typical subclasses requiring serializability will 60 * define a {@code readObject} method that restores this to a known 61 * initial state upon deserialization. 62 * 63 * <h3>Usage</h3> 64 * 65 * <p>To use this class as the basis of a synchronizer, redefine the 66 * following methods, as applicable, by inspecting and/or modifying 67 * the synchronization state using {@link #getState}, {@link 68 * #setState} and/or {@link #compareAndSetState}: 69 * 70 * <ul> 71 * <li> {@link #tryAcquire} 72 * <li> {@link #tryRelease} 73 * <li> {@link #tryAcquireShared} 74 * <li> {@link #tryReleaseShared} 75 * <li> {@link #isHeldExclusively} 76 * </ul> 77 * 78 * Each of these methods by default throws {@link 79 * UnsupportedOperationException}. Implementations of these methods 80 * must be internally thread-safe, and should in general be short and 81 * not block. Defining these methods is the <em>only</em> supported 82 * means of using this class. All other methods are declared 83 * {@code final} because they cannot be independently varied. 84 * 85 * <p>You may also find the inherited methods from {@link 86 * AbstractOwnableSynchronizer} useful to keep track of the thread 87 * owning an exclusive synchronizer. You are encouraged to use them 88 * -- this enables monitoring and diagnostic tools to assist users in 89 * determining which threads hold locks. 90 * 91 * <p>Even though this class is based on an internal FIFO queue, it 92 * does not automatically enforce FIFO acquisition policies. The core 93 * of exclusive synchronization takes the form: 94 * 95 * <pre> 96 * Acquire: 97 * while (!tryAcquire(arg)) { 98 * <em>enqueue thread if it is not already queued</em>; 99 * <em>possibly block current thread</em>; 100 * } 101 * 102 * Release: 103 * if (tryRelease(arg)) 104 * <em>unblock the first queued thread</em>; 105 * </pre> 106 * 107 * (Shared mode is similar but may involve cascading signals.) 108 * 109 * <p id="barging">Because checks in acquire are invoked before 110 * enqueuing, a newly acquiring thread may <em>barge</em> ahead of 111 * others that are blocked and queued. However, you can, if desired, 112 * define {@code tryAcquire} and/or {@code tryAcquireShared} to 113 * disable barging by internally invoking one or more of the inspection 114 * methods, thereby providing a <em>fair</em> FIFO acquisition order. 115 * In particular, most fair synchronizers can define {@code tryAcquire} 116 * to return {@code false} if {@link #hasQueuedPredecessors} (a method 117 * specifically designed to be used by fair synchronizers) returns 118 * {@code true}. Other variations are possible. 119 * 120 * <p>Throughput and scalability are generally highest for the 121 * default barging (also known as <em>greedy</em>, 122 * <em>renouncement</em>, and <em>convoy-avoidance</em>) strategy. 123 * While this is not guaranteed to be fair or starvation-free, earlier 124 * queued threads are allowed to recontend before later queued 125 * threads, and each recontention has an unbiased chance to succeed 126 * against incoming threads. Also, while acquires do not 127 * "spin" in the usual sense, they may perform multiple 128 * invocations of {@code tryAcquire} interspersed with other 129 * computations before blocking. This gives most of the benefits of 130 * spins when exclusive synchronization is only briefly held, without 131 * most of the liabilities when it isn't. If so desired, you can 132 * augment this by preceding calls to acquire methods with 133 * "fast-path" checks, possibly prechecking {@link #hasContended} 134 * and/or {@link #hasQueuedThreads} to only do so if the synchronizer 135 * is likely not to be contended. 136 * 137 * <p>This class provides an efficient and scalable basis for 138 * synchronization in part by specializing its range of use to 139 * synchronizers that can rely on {@code int} state, acquire, and 140 * release parameters, and an internal FIFO wait queue. When this does 141 * not suffice, you can build synchronizers from a lower level using 142 * {@link java.util.concurrent.atomic atomic} classes, your own custom 143 * {@link java.util.Queue} classes, and {@link LockSupport} blocking 144 * support. 145 * 146 * <h3>Usage Examples</h3> 147 * 148 * <p>Here is a non-reentrant mutual exclusion lock class that uses 149 * the value zero to represent the unlocked state, and one to 150 * represent the locked state. While a non-reentrant lock 151 * does not strictly require recording of the current owner 152 * thread, this class does so anyway to make usage easier to monitor. 153 * It also supports conditions and exposes 154 * one of the instrumentation methods: 155 * 156 * <pre> {@code 157 * class Mutex implements Lock, java.io.Serializable { 158 * 159 * // Our internal helper class 160 * private static class Sync extends AbstractQueuedSynchronizer { 161 * // Reports whether in locked state 162 * protected boolean isHeldExclusively() { 163 * return getState() == 1; 164 * } 165 * 166 * // Acquires the lock if state is zero 167 * public boolean tryAcquire(int acquires) { 168 * assert acquires == 1; // Otherwise unused 169 * if (compareAndSetState(0, 1)) { 170 * setExclusiveOwnerThread(Thread.currentThread()); 171 * return true; 172 * } 173 * return false; 174 * } 175 * 176 * // Releases the lock by setting state to zero 177 * protected boolean tryRelease(int releases) { 178 * assert releases == 1; // Otherwise unused 179 * if (getState() == 0) throw new IllegalMonitorStateException(); 180 * setExclusiveOwnerThread(null); 181 * setState(0); 182 * return true; 183 * } 184 * 185 * // Provides a Condition 186 * Condition newCondition() { return new ConditionObject(); } 187 * 188 * // Deserializes properly 189 * private void readObject(ObjectInputStream s) 190 * throws IOException, ClassNotFoundException { 191 * s.defaultReadObject(); 192 * setState(0); // reset to unlocked state 193 * } 194 * } 195 * 196 * // The sync object does all the hard work. We just forward to it. 197 * private final Sync sync = new Sync(); 198 * 199 * public void lock() { sync.acquire(1); } 200 * public boolean tryLock() { return sync.tryAcquire(1); } 201 * public void unlock() { sync.release(1); } 202 * public Condition newCondition() { return sync.newCondition(); } 203 * public boolean isLocked() { return sync.isHeldExclusively(); } 204 * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } 205 * public void lockInterruptibly() throws InterruptedException { 206 * sync.acquireInterruptibly(1); 207 * } 208 * public boolean tryLock(long timeout, TimeUnit unit) 209 * throws InterruptedException { 210 * return sync.tryAcquireNanos(1, unit.toNanos(timeout)); 211 * } 212 * }}</pre> 213 * 214 * <p>Here is a latch class that is like a 215 * {@link java.util.concurrent.CountDownLatch CountDownLatch} 216 * except that it only requires a single {@code signal} to 217 * fire. Because a latch is non-exclusive, it uses the {@code shared} 218 * acquire and release methods. 219 * 220 * <pre> {@code 221 * class BooleanLatch { 222 * 223 * private static class Sync extends AbstractQueuedSynchronizer { 224 * boolean isSignalled() { return getState() != 0; } 225 * 226 * protected int tryAcquireShared(int ignore) { 227 * return isSignalled() ? 1 : -1; 228 * } 229 * 230 * protected boolean tryReleaseShared(int ignore) { 231 * setState(1); 232 * return true; 233 * } 234 * } 235 * 236 * private final Sync sync = new Sync(); 237 * public boolean isSignalled() { return sync.isSignalled(); } 238 * public void signal() { sync.releaseShared(1); } 239 * public void await() throws InterruptedException { 240 * sync.acquireSharedInterruptibly(1); 241 * } 242 * }}</pre> 243 * 244 * @since 1.5 245 * @author Doug Lea 246 */
总结一下关键信息
AQS其实就是一个可以给我们实现锁的框架
内部实现的关键是:先进先出的队列,state状态
定义了内部类ConditionObject
拥有两种线程模式
独占模式
共享模式
在lock包中的相关锁(常用的有ReentrantLock、ReadWriteLock)都是基于AQS来构建
一般我们叫AQS为同步器
二、简单看看AQS
上面也提到了AQS里面最重要的是状态和队列,我们接下来就看看其源码是怎么样的。
2.1 同步状态
使用volatile修饰实现线程可见性

修改state值得时候使用CAS算法来实现

2.2 先进先出队列
这个队列被称为:CLH队列(三个名字组成),是一个双向队列

看看他队列源码得组成:
1 static final class Node { 2 3 // 共享 4 static final Node SHARED = new Node(); 5 6 // 独占 7 static final Node EXCLUSIVE = null; 8 9 // 线程被取消了 10 static final int CANCELLED = 1; 11 12 // 后继线程需要唤醒 13 static final int SIGNAL = -1; 14 15 // 等待condition唤醒 16 static final int CONDITION = -2; 17 18 // 共享式同步状态获取将会无条件地传播下去(没看懂) 19 static final int PROPAGATE = -3; 20 21 22 // 初始为0,状态是上面的几种 23 volatile int waitStatus; 24 25 // 前置节点 26 volatile Node prev; 27 28 // 后继节点 29 volatile Node next; 30 31 32 volatile Thread thread; 33 34 35 Node nextWaiter; 36 37 final boolean isShared() { 38 return nextWaiter == SHARED; 39 } 40 41 42 final Node predecessor() throws NullPointerException { 43 Node p = prev; 44 if (p == null) 45 throw new NullPointerException(); 46 else 47 return p; 48 } 49 50 Node() { // Used to establish initial head or SHARED marker 51 } 52 53 Node(Thread thread, Node mode) { // Used by addWaiter 54 this.nextWaiter = mode; 55 this.thread = thread; 56 } 57 58 Node(Thread thread, int waitStatus) { // Used by Condition 59 this.waitStatus = waitStatus; 60 this.thread = thread; 61 } 62 }
2.3 acquire方法
获取独占锁得过程就是在acquire定义得,该方法用到了设计模式,由子类实现的

过程:acquire(int)尝试获取资源,如果获取失败,将线程插入等待队列。插入队列后,acquire(int)并没有放弃获取资源,而是根据前置节点的状态判断是否应该继续获取资源,如果前置节点是头节点,头节点,继续尝试获取资源,如果前置节点是SIGNAL状态,就中断当前线程,否则继续尝试获取资源。直到当前线程被park()或者获取到资源,acquire(int)结束。
2.4 release方法
释放独占锁的过程就是在acquire定义的,该方法也用到了模板设计模式,由子类实现

首先调用子类的tryRelease()方法释放锁,然后唤醒后继节点,在唤醒的过程中,需要判断后继节点是否满足,如果后继节点不为且不是作废状态,则唤醒这个后继节点,否则从tail节点向前寻找合适的节点,如果找到,则唤醒。
三、最后
总结一下AQS到底是啥玩意儿吧:
juc包中有很多可阻塞的类都是基于AQS构建的
AQS可以说是一个给予实现同步锁、同步器的一个框架,很多实现类都在它的基础上构建的
在AQS中实现了对等待队列的默认实现,子类只要重写部分代码即可实现(大量用到了模板代码)

浙公网安备 33010602011771号