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 &quot;understand&quot; 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  * &quot;spin&quot; 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中实现了对等待队列的默认实现,子类只要重写部分代码即可实现(大量用到了模板代码)

 

posted @ 2019-09-20 11:17  chyblogs  阅读(334)  评论(0)    收藏  举报