(1)AQS的定位?
(2)AQS的重要组成部分?
(3)AQS运用的设计模式?
(4)AQS的总体流程?
AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。
在之前的章节中,我们一起学习了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码,今天我们一起来对AQS做个总结。
AQS中定义了一个状态变量state,它有以下两种使用方法:
(1)互斥锁
当AQS只实现为互斥锁的时候,每次只要原子更新state的值从0变为1成功了就获取了锁,可重入是通过不断把state原子更新加1实现的。
(2)互斥锁 + 共享锁
当AQS需要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。
互斥锁是一种独占锁,每次只允许一个线程独占,且当一个线程独占时,其它线程将无法再获取互斥锁及共享锁,但是它自己可以获取共享锁。
共享锁同时允许多个线程占有,只要有一个线程占有了共享锁,所有线程(包括自己)都将无法再获取互斥锁,但是可以获取共享锁。
AQS中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。
AQS中还有另一个非常重要的内部类ConditionObject,它实现了Condition接口,主要用于实现条件锁。
ConditionObject中也维护了一个队列,这个队列主要用于等待条件的成立,当条件成立时,其它线程将signal这个队列中的元素,将其移动到AQS的队列中,等待占有锁的线程释放锁后被唤醒。
Condition典型的运用场景是在BlockingQueue中的实现,当队列为空时,获取元素的线程阻塞在notEmpty条件上,一旦队列中添加了一个元素,将通知notEmpty条件,将其队列中的元素移动到AQS队列中等待被唤醒。
AQS这个抽象类把模板方法设计模式运用地炉火纯青,它里面定义了一系列的模板方法,比如下面这些:
// 获取互斥锁public final void acquire(int arg) { // tryAcquire(arg)需要子类实现 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();}// 获取互斥锁可中断public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // tryAcquire(arg)需要子类实现 if (!tryAcquire(arg)) doAcquireInterruptibly(arg);} // 获取共享锁public final void acquireShared(int arg) { // tryAcquireShared(arg)需要子类实现 if (tryAcquireShared(arg) < 0) doAcquireShared(arg);}// 获取共享锁可中断public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // tryAcquireShared(arg)需要子类实现 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);}// 释放互斥锁public final boolean release(int arg) { // tryRelease(arg)需要子类实现 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false;}// 释放共享锁public final boolean releaseShared(int arg) { // tryReleaseShared(arg)需要子类实现 if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false;}
获取锁、释放锁的这些方法基本上都穿插在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码解析中了,现在看他们是不是舒服多了,如果一开始就看这些源码,难免会很晕。
上面一起学习了AQS中几个重要的模板方法,下面我们再一起学习下几个需要子类实现的方法:
// 互斥模式下使用:尝试获取锁protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException();}// 互斥模式下使用:尝试释放锁protected boolean tryRelease(int arg) { throw new UnsupportedOperationException();}// 共享模式下使用:尝试获取锁protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException();}// 共享模式下使用:尝试释放锁protected boolean tryReleaseShared(int arg) { throw new UnsupportedOperationException();}// 如果当前线程独占着锁,返回trueprotected boolean isHeldExclusively() { throw new UnsupportedOperationException();}
这几个方法为什么不直接定义成抽象方法呢?
因为子类只要实现这几个方法中的一部分就可以实现一个同步器了,所以不需要定义成抽象方法。
今天我们大概讲了下AQS中几个重要的组成部分,搞明白了这几个结构,AQS对你将没有任何秘密可言,当然面试的时候能把这几个点答清楚,面试官也会眼前一亮的。
(1)状态变量state;
(2)AQS队列;
(3)Condition队列;
(4)模板方法;
(5)需要子类实现的方法;
经过前面的学习,您能简要描述一下AQS获取互斥锁的大体流程吗?
这里彤哥就不作答了,相信学习完前面的内容,答这道题不是个问题了,答不上来的还需要把下面的推荐阅读好好多看几遍^^当然,能顺手点下小广告那就更完美了^^