java exclusiveThread_Java多线程--AQS

ReentrantLock和AQS的关系

首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁,是个什么样的:

1 ReentrantLock reentrantLock = newReentrantLock();2 reentrantLock.lock();3 //业务代码

4 reentrantLock.unlock();

上面那段代码就是搞一个Lock对象,然后加锁和释放锁。那么,这个跟AQS有啥关系?关系大了去了,因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。上一部分源码:

1 public class ReentrantLock implementsLock, java.io.Serializable {2 private static final long serialVersionUID = 7373984872572414699L;3 /**Synchronizer providing all implementation mechanics*/

4 private finalSync sync;5

6 /**

7 * Base of synchronization control for this lock. Subclassed8 * into fair and nonfair versions below. Uses AQS state to9 * represent the number of holds on the lock.10 */

11 abstract static class Sync extendsAbstractQueuedSynchronizer {12 private static final long serialVersionUID = -5179523762034025860L;13

14 /**

15 * Performs {@linkLock#lock}. The main reason for subclassing16 * is to allow fast path for nonfair version.17 */

18 abstract voidlock();19

20 /**

21 * Performs non-fair tryLock. tryAcquire is implemented in22 * subclasses, but both need nonfair try for trylock method.23 */

24 final boolean nonfairTryAcquire(intacquires) {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(intreleases) {44 int c = getState() -releases;45 if (Thread.currentThread() !=getExclusiveOwnerThread())46 throw newIllegalMonitorStateException();47 boolean free = false;48 if (c == 0) {49 free = true;50 setExclusiveOwnerThread(null);51 }52 setState(c);53 returnfree;54 }55

56 protected final booleanisHeldExclusively() {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 finalConditionObject newCondition() {63 return newConditionObject();64 }65

66 //Methods relayed from outer class

67

68 finalThread getOwner() {69 return getState() == 0 ? null: getExclusiveOwnerThread();70 }71

72 final intgetHoldCount() {73 return isHeldExclusively() ? getState() : 0;74 }75

76 final booleanisLocked() {77 return getState() != 0;78 }79

80 /**

81 * Reconstitutes the instance from a stream (that is, deserializes it).82 */

83 private voidreadObject(java.io.ObjectInputStream s)84 throwsjava.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 classAbstractQueuedSynchronizer2 extendsAbstractOwnableSynchronizer3 implementsjava.io.Serializable {4

5 /**

6 * The thread that enqueued this node. Initialized on7 * construction and nulled out after use.8 */

9 volatileThread thread;10

11 /**

12 * The synchronization state.13 */

14 private volatile intstate;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 implementsLock, java.io.Serializable {2 /**

3 * Sync object for non-fair locks4 */

5 static final class NonfairSync extendsSync {6 private static final long serialVersionUID = 7316153563782823691L;7

8 /**

9 * Performs lock. Try immediate barge, backing up to normal10 * acquire on failure.11 */

12 final voidlock() {13 if (compareAndSetState(0, 1))14 setExclusiveOwnerThread(Thread.currentThread());15 else

16 acquire(1);17 }18

19 protected final boolean tryAcquire(intacquires) {20 returnnonfairTryAcquire(acquires);21 }22 }23 }24

25 public abstract classAbstractQueuedSynchronizer26 extendsAbstractOwnableSynchronizer27 implementsjava.io.Serializable {28

29 /**

30 * Acquires in exclusive mode, ignoring interrupts. Implemented31 * by invoking at least once {@link#tryAcquire},32 * returning on success. Otherwise the thread is queued, possibly33 * repeatedly blocking and unblocking, invoking {@link

34 * #tryAcquire} until success. This method can be used35 * to implement method {@linkLock#lock}.36 *37 *@paramarg the acquire argument. This value is conveyed to38 * {@link#tryAcquire} but is otherwise uninterpreted and39 * can represent anything you like.40 */

41 public final void acquire(intarg) {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 *

Method documentation for this class describes mechanics,7 * not behavioral specifications from the point of view of Lock8 * and Condition users. Exported versions of this class will in9 * general need to be accompanied by documentation describing10 * condition semantics that rely on those of the associated11 * {@codeAbstractQueuedSynchronizer}.12 *13 *

This class is Serializable, but all fields are transient,14 * so deserialized conditions have no waiters.15 */

16public class ConditionObject implementsCondition, java.io.Serializable {17 private static final long serialVersionUID = 1173984872572414699L;18 /**First node of condition queue.*/

19 private transientNode firstWaiter;20 /**Last node of condition queue.*/

21 private transientNode lastWaiter;22

23 /**

24 * Creates a new {@codeConditionObject} instance.25 */

26 publicConditionObject() { }27

28 //Internal methods

29

30 /**

31 * Adds a new waiter to wait queue.32 *@returnits new wait node33 */

34 privateNode 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 = newNode(Thread.currentThread(), Node.CONDITION);42 if (t == null)43 firstWaiter =node;44 else

45 t.nextWaiter =node;46 lastWaiter =node;47 returnnode;48 }49 }

接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁,他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!

1 public class ReentrantLock implementsLock, java.io.Serializable {2 /**

3 * Attempts to release this lock.4 *5 *

If the current thread is the holder of this lock then the hold6 * count is decremented. If the hold count is now zero then the lock7 * is released. If the current thread is not the holder of this8 * lock then {@linkIllegalMonitorStateException} is thrown.9 *10 *@throwsIllegalMonitorStateException if the current thread does not11 * hold this lock12 */

13 public voidunlock() {14 sync.release(1);15 }16 }17

18 public abstract classAbstractQueuedSynchronizer19 extendsAbstractOwnableSynchronizer20 implementsjava.io.Serializable {21 public final boolean release(intarg) {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变量、加锁线程、等待队列等并发中的核心组件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值