ReentrantLock和AQS的关系
首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁,是个什么样的:
1 ReentrantLock reentrantLock = new ReentrantLock(); 2 reentrantLock.lock(); 3 //业务代码 4 reentrantLock.unlock();
上面那段代码就是搞一个Lock对象,然后加锁和释放锁。那么,这个跟AQS有啥关系?关系大了去了,因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。上一部分源码:
1 public class ReentrantLock implements Lock, java.io.Serializable { 2 private static final long serialVersionUID = 7373984872572414699L; 3 /** Synchronizer providing all implementation mechanics */ 4 private final Sync sync; 5 6 /** 7 * Base of synchronization control for this lock. Subclassed 8 * into fair and nonfair versions below. Uses AQS state to 9 * represent the number of holds on the lock. 10 */ 11 abstract static class Sync extends AbstractQueuedSynchronizer { 12 private static final long serialVersionUID = -5179523762034025860L; 13 14 /** 15 * Performs {@link Lock#lock}. The main reason for subclassing 16 * is to allow fast path for nonfair version. 17 */ 18 abstract void lock(); 19 20 /** 21 * Performs non-fair tryLock. tryAcquire is implemented in 22 * subclasses, but both need nonfair try for trylock method. 23 */ 24 final boolean nonfairTryAcquire(int acquires) { 25 final Thread current = Thread.currentThread(); 26 int c = getState(); 27 if (c == 0) { 28 if (compareAndSetState(0, acquires)) { 29 setExclusiveOwnerThread(current); 30 return true; 31 } 32 } 33 else if (current == getExclusiveOwnerThread()) { 34 int nextc = c + acquires; 35 if (nextc < 0) // overflow 36 throw new Error("Maximum lock count exceeded"); 37 setState(nextc); 38 return true; 39 } 40 return false; 41 } 42 43 protected final boolean tryRelease(int releases) { 44 int c = getState() - releases; 45 if (Thread.currentThread() != getExclusiveOwnerThread()) 46 throw new IllegalMonitorStateException(); 47 boolean free = false; 48 if (c == 0) { 49 free = true; 50 setExclusiveOwnerThread(null); 51 } 52 setState(c); 53 return free; 54 } 55 56 protected final boolean isHeldExclusively() { 57 // While we must in general read state before owner, 58 // we don't need to do so to check if current thread is owner 59 return getExclusiveOwnerThread() == Thread.currentThread(); 60 } 61 62 final ConditionObject newCondition() { 63 return new ConditionObject(); 64 } 65 66 // Methods relayed from outer class 67 68 final Thread getOwner() { 69 return getState() == 0 ? null : getExclusiveOwnerThread(); 70 } 71 72 final int getHoldCount() { 73 return isHeldExclusively() ? getState() : 0; 74 } 75 76 final boolean isLocked() { 77 return getState() != 0; 78 } 79 80 /** 81 * Reconstitutes the instance from a stream (that is, deserializes it). 82 */ 83 private void readObject(java.io.ObjectInputStream s) 84 throws java.io.IOException, ClassNotFoundException { 85 s.defaultReadObject(); 86 setState(0); // reset to unlocked state 87 } 88 } 89 }
说白了,ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。
ReentrantLock加锁和释放锁的底层原理
现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情呢?
1 public abstract class AbstractQueuedSynchronizer 2 extends AbstractOwnableSynchronizer 3 implements java.io.Serializable { 4 5 /** 6 * The thread that enqueued this node. Initialized on 7 * construction and nulled out after use. 8 */ 9 volatile Thread thread; 10 11 /** 12 * The synchronization state. 13 */ 14 private volatile int state; 15 16 }
这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。
AQS就是并发包里的一个核心组件,里面有state变量、加锁线程变量等核心的东西,维护了加锁状态。ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。这个ReentrantLock之所以用Reentrant打头,意思就是他是一个可重入锁。意思就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。大家看明白了那个state变量之后,就知道了如何进行可重入加锁!其实每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化,实现原理如下:
1 public class ReentrantLock implements Lock, java.io.Serializable { 2 /** 3 * Sync object for non-fair locks 4 */ 5 static final class NonfairSync extends Sync { 6 private static final long serialVersionUID = 7316153563782823691L; 7 8 /** 9 * Performs lock. Try immediate barge, backing up to normal 10 * acquire on failure. 11 */ 12 final void lock() { 13 if (compareAndSetState(0, 1)) 14 setExclusiveOwnerThread(Thread.currentThread()); 15 else 16 acquire(1); 17 } 18 19 protected final boolean tryAcquire(int acquires) { 20 return nonfairTryAcquire(acquires); 21 } 22 } 23 } 24 25 public abstract class AbstractQueuedSynchronizer 26 extends AbstractOwnableSynchronizer 27 implements java.io.Serializable { 28 29 /** 30 * Acquires in exclusive mode, ignoring interrupts. Implemented 31 * by invoking at least once {@link #tryAcquire}, 32 * returning on success. Otherwise the thread is queued, possibly 33 * repeatedly blocking and unblocking, invoking {@link 34 * #tryAcquire} until success. This method can be used 35 * to implement method {@link Lock#lock}. 36 * 37 * @param arg the acquire argument. This value is conveyed to 38 * {@link #tryAcquire} but is otherwise uninterpreted and 39 * can represent anything you like. 40 */ 41 public final void acquire(int arg) { 42 if (!tryAcquire(arg) && 43 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 44 selfInterrupt(); 45 } 46 47 }
接着,如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?我们来看看锁的互斥是如何实现的,线程2跑过来一下看到state的值不是0,所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。接着,线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了,所以大家可以看到,AQS是如此的核心。AQS内部还有一个等待队列,专门放那些加锁失败的线程。
1 /** 2 * Condition implementation for a {@link 3 * AbstractQueuedSynchronizer} serving as the basis of a {@link 4 * Lock} implementation. 5 * 6 * <p>Method documentation for this class describes mechanics, 7 * not behavioral specifications from the point of view of Lock 8 * and Condition users. Exported versions of this class will in 9 * general need to be accompanied by documentation describing 10 * condition semantics that rely on those of the associated 11 * {@code AbstractQueuedSynchronizer}. 12 * 13 * <p>This class is Serializable, but all fields are transient, 14 * so deserialized conditions have no waiters. 15 */ 16 public class ConditionObject implements Condition, java.io.Serializable { 17 private static final long serialVersionUID = 1173984872572414699L; 18 /** First node of condition queue. */ 19 private transient Node firstWaiter; 20 /** Last node of condition queue. */ 21 private transient Node lastWaiter; 22 23 /** 24 * Creates a new {@code ConditionObject} instance. 25 */ 26 public ConditionObject() { } 27 28 // Internal methods 29 30 /** 31 * Adds a new waiter to wait queue. 32 * @return its new wait node 33 */ 34 private Node addConditionWaiter() { 35 Node t = lastWaiter; 36 // If lastWaiter is cancelled, clean out. 37 if (t != null && t.waitStatus != Node.CONDITION) { 38 unlinkCancelledWaiters(); 39 t = lastWaiter; 40 } 41 Node node = new Node(Thread.currentThread(), Node.CONDITION); 42 if (t == null) 43 firstWaiter = node; 44 else 45 t.nextWaiter = node; 46 lastWaiter = node; 47 return node; 48 } 49 }
接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁,他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!
1 public class ReentrantLock implements Lock, java.io.Serializable { 2 /** 3 * Attempts to release this lock. 4 * 5 * <p>If the current thread is the holder of this lock then the hold 6 * count is decremented. If the hold count is now zero then the lock 7 * is released. If the current thread is not the holder of this 8 * lock then {@link IllegalMonitorStateException} is thrown. 9 * 10 * @throws IllegalMonitorStateException if the current thread does not 11 * hold this lock 12 */ 13 public void unlock() { 14 sync.release(1); 15 } 16 } 17 18 public abstract class AbstractQueuedSynchronizer 19 extends AbstractOwnableSynchronizer 20 implements java.io.Serializable { 21 public final boolean release(int arg) { 22 if (tryRelease(arg)) { 23 Node h = head; 24 if (h != null && h.waitStatus != 0) 25 unparkSuccessor(h); 26 return true; 27 } 28 return false; 29 } 30 }
接下来,会从等待队列的队头唤醒线程2重新尝试加锁。好!线程2现在就重新尝试加锁,这时还是用CAS操作将state从0变为1,此时就会成功,成功之后代表加锁成功,就会将state设置为1。此外,还要把“加锁线程”设置为线程2自己,同时线程2自己就从等待队列中出队了。
其实一句话总结:AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。