AQS和ReentrantLock源码学习
1:AQS设计思想
一言以蔽之,AQS的设计思想就是,获取同步资源的成功的线程能够继续运行,而获取同步资源失败的线程要进去等待队列去等待。
1.1:内部节点类介绍
static final class Node { /** Marker to indicate a node is waiting in shared mode */ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE = null; /** waitStatus value to indicate thread has cancelled */ //代表当前节点的线程会被取消或者中断了 static final int CANCELLED = 1; /** waitStatus value to indicate successor's thread needs unparking */ //该状态,暗示当前线程节点的下一个节点需要在当前节点释放锁后,要unpark static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition */ //代表当前节点时处于condition的队列中,这是因为condition接口内部的等待队列的节点时是 //复用AQS内部node类,所以增加了该状态代表线程节点是处于condition队列中。 static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ static final int PROPAGATE = -3; /** * Status field, taking on only the values: * SIGNAL: The successor of this node is (or will soon be) * blocked (via park), so the current node must * unpark its successor when it releases or * cancels. To avoid races, acquire methods must * first indicate they need a signal, * then retry the atomic acquire, and then, * on failure, block. * CANCELLED: This node is cancelled due to timeout or interrupt. * Nodes never leave this state. In particular, * a thread with cancelled node never again blocks. * CONDITION: This node is currently on a condition queue. * It will not be used as a sync queue node * until transferred, at which time the status * will be set to 0. (Use of this value here has * nothing to do with the other uses of the * field, but simplifies mechanics.) * PROPAGATE: A releaseShared should be propagated to other * nodes. This is set (for head node only) in * doReleaseShared to ensure propagation * continues, even if other operations have * since intervened. * 0: None of the above * * The values are arranged numerically to simplify use. * Non-negative values mean that a node doesn't need to * signal. So, most code doesn't need to check for particular * values, just for sign. * * The field is initialized to 0 for normal sync nodes, and * CONDITION for condition nodes. It is modified using CAS * (or when possible, unconditional volatile writes). */ //代表当前节点状态的int变量。 volatile int waitStatus; /** * Link to predecessor,指向前驱节点 */ volatile Node prev; /** * Link to the successor node,指向后继节点 */ volatile Node next; /** * The thread that enqueued this node. Initialized on * construction and nulled out after use. */ volatile Thread thread; /** * Link to next node waiting on condition, *如果当前线程节点被在condition接口的队列上,则该nextWaiter指向condition队列中的后继节点 */ Node nextWaiter; /** * Returns true if node is waiting in shared mode. */ final boolean isShared() { return nextWaiter == SHARED; } /** * Returns previous node, or throws NullPointerException if null. * Use when predecessor cannot be null. The null check could * be elided, but is present to help the VM. * * @return the predecessor of this node */ final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
1.2:插入等待队列方法介绍
//加入等待队列的方法 /** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) {//死循环 Node t = tail;//取出当前队列的尾部 if (t == null) { // Must initialize,如果队列为null,则新建一个线程为null的节点, if (compareAndSetHead(new Node())) tail = head; } else {//如果不为空,则将待插入节点前驱指向尾部, node.prev = t; //通过CAS算法判断t是不是依然为尾部,因为该方法没有被同步, //有可能被其他线程串改了,如果依然为尾部即tail引用和t引用指向同一个对象 //则将交换使得tail指向新的node。然后将t的后继为当前新的尾部, //因为此时t指向的为tail(当前node被更新了tail了)的前一个节点。 if (compareAndSetTail(t, node)) { t.next = node; return t; } //如果CAS操作不成功,则循环继续尝试插入。 } } } /** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; //逻辑与上面类是。先尝试简单的直接插入,如果成功则好, //如果不成功在使用enq方法直到成功。 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
1.3:唤醒后继节点的方法
/** * Wakes up node's successor, if one exists. *唤醒当前节点的后继节点,如果后继节点为null,或者状态waitStatus大于0 *则后tail开始向前遍历,找到里当前节点最近的且不为null且waitStatue大于0的后继节点, *然后唤醒 * @param node the node */ private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; //如果当前节点的ws小于0,值置0 //因为,当前节点计算unpark后继节点,代表当前节点已经释放锁了, //已经要出等待队列了。所以要置0! if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; //如果后继节点为null,或者waitStatus大于0待变被中断或者取消状态, //则也不能unpark if (s == null || s.waitStatus > 0) { s = null; //从尾部tail向前遍历,直到找到找到不为null,且waitStats小于0的节点为止 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } //唤醒unpark前面找到的节点。 if (s != null) LockSupport.unpark(s.thread); }
2:ReentrantLock源码分析
ReentrantLock是实现了AQS机制的一种可重入锁,这种锁公平和非公平锁两种模式。一言以蔽之单个线程或者多线程交替执行的时候和AQS的队列无关的,在虚拟机内部就解决同步问题。当竞争执行的时候才会在队列等待,使用park和unpark在操作系统级别解决同步问题。其实,ReentrantLock并发机制和操作系统的PV机制原理相同。都需要一个整形的信号量,一个等待队列,以及原子性的P操作和V操作。只不过PV操作原子性由OS保证。而AQS的原子性由CAS去保证。
2.1:公平锁和非公平锁的区别
//公平锁类 /** * Sync object for fair locks */ static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; //公平锁里的lock方法,执行acquire(1)方法, final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ //在acquire的方法里使用模板模式,在调用各自类的tryAcquire(1)方法 protected final boolean tryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取当前锁的状态即AQS的state int c = getState(); //如果c==0,意味着锁处于自由状态,即同步资源处于可使用抓状态 if (c == 0) { //进入hasQueuedPredecessors()方法,当该方法返回fasle时 //才会继续compareAndSetState(0, acquires)方法。 //进入hasQueuedPredecessors()方法看看,如果该方法返回false //达表不存在已经等待时间更长的线程节点,则会继续判断compareAndSetState(0, acquires:1) //该方法CAS操作如果c还=0,则state置1,代表当前线程获取锁成功,然后设置拥有线程 //并return true。 //这里体现的是公平锁特性,因为虽然当前同步资源为自由状态, //但是我们还要判断是不是之前已经有线程在等待了,如果没有才能获取, //否则返回false,进入等待队列等待。 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //如果当前同步资源处于锁定状态,是不是一定不能获取锁尼? //不是的!因为我们是可重入锁,当当前线程与拥有锁的线程相等,也返回true,线程继续运行 else if (current == getExclusiveOwnerThread()) { //可重入锁,增加state值 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } //非公平锁类 /** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { //非公平锁,上就直接尝试把state通过CAS算法置1, if (compareAndSetState(0, 1)) //如果CAS成功,代表此时同步资源处于自由状态 //则直接将当前线程作为同步资源的拥有者。 setExclusiveOwnerThread(Thread.currentThread()); else //如果不成功,在acqure(1),我们进去AQS的acquire看看 acquire(1); } //直接调用父类Sync的里面方法的非公平锁方法 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } //这是公平锁和非公平锁的父类Sync的父类AQS里面的acquire方法。 //该方案通过模板模式,在调用子类的tryAcquire方法来实现公平锁和非公平锁的功能 //我们可以看到tryAcquire里面在公平锁和非公平锁的实现不同 public final void acquire(int arg) { //两个条件相与,第一个条件为tryAcquire(arg),我们返回锁内部的tryAcquire看看 //当tryAcquire返回true,代表已经获取锁了,则当前条件不成立,直接从acqure中返回 //如果返回false,则待变着当前线程要被加入等待队列,阻塞。则后一个条件要执行,将当前线程加入等待队列 //同时park当前线程。 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //这也是来自AQS内部方法:主要功能是判断是不是还有等待时间更长的线程节点存在于队列中 /** * Queries whether any threads have been waiting to acquire longer * than the current thread. * @return {@code true} if there is a queued thread preceding the * current thread, and {@code false} if the current thread * is at the head of the queue or the queue is empty *如果,存在等待时间更长的线程节点,则返回true;如果当前线程为head或者等待队列为空,返回false * @since 1.7 */ public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); } //这是非公平锁的父类Sync里面实现的nonfairTryAcquire方法 /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { //获取当前线程, final Thread current = Thread.currentThread(); //获取当前同步资源状态 int c = getState(); //如果处于自由状态 if (c == 0) { //通过CAS算法直接置1,同时修改当前锁的持有线程 //没有要不要再继续判断有没有已经处于等待队列的线程的步骤了 //这也是非公平体现的地方,直接获取锁,不管是不是等待队列已经有等待等长时间的线程了 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); //并放回true return true; } } //不为0,判断是不是已经获取锁的线程,逻辑和公平锁类似 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
最主要的区别就是:公平锁在判断同步资源处于自由状态后,还需要在进行是不是队列中已经存在等待时间更长的线程,不存在才能进行CAS操作,获取锁。而非公平锁,只要当前同步资源处于自由状态,则立马进行CAS操作尝试获取锁,不管是不是还有没有线程等待等待更久了。
流程图如上:转载来自https://blog.csdn.net/java_lyvee/article/details/98966684
3:总结
ReentrantLock是利用AQS机制来实现的一种可重入,公平和非公平两种方法的锁。当在单线程或是或线程交替执行时,不会通过等待队列,直接再JVM层面就保证运行;如果出现了多线程竞争执行,则通过内部的等待队列来管理线程,保证线程的同步。