前提概要
之前的文章中会涉及到了相关AQS的原理和相关源码的分析,所谓实践是检验真理的唯一标准!接下来就让我们活化一下AQS技术,主要针对于自己动手实现一个AQS同步器 。定义MyLock实现Lock
Doug Lea大神在JDK1.5编写了一个Lock接口,里面定义了实现一个锁的基本方法,我们只需编写一个MyLock类实现这个接口就好 。
class MyLock implements Lock {/*** 加锁 。如果不成功则进入等待队列*/@Overridepublic void lock() {}/*** 加锁(可被interrupt)*/@Overridepublic void lockInterruptibly() throws InterruptedException {}/*** 尝试加锁*/@Overridepublic boolean tryLock() {}/*** 加锁 带超时的*/@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {}/*** 释放锁*/@Overridepublic void unlock() {}/*** 返回一个条件变量(不在本案例谈论)*/@Overridepublic Condition newCondition() {}}复制代码定义好MyLock后,接下来就是实现各个方法的逻辑,达到真正的用于线程间sync互斥的需求 。自定义一个MySync继承自AQS接下来我们需要自定义一个继承自AQS的MySync 。实现自定义的MySync前,先了解AQS内部的一些基本概念 。在AQS中主要的一些成员属性如下:

文章插图
- state:用于标记资源状态,如果为0表示资源没有被占用,可以加锁成功 。如果大于0表示资源已经被占用,然后根据自己的定义去实现是否允许对共享资源进行操作 。比如:ReentrantLock的实现方式是当state大于0,那么表示已经有线程获得锁了,我们都知道ReentrantLock是可重入的,其原理就是当有线程次进入同一个lock标记的临界区时 。先判断这个线程是否是获得锁的那个线程,如果是,state会+1,此时state会等于2 。当unlock时,会一层一层地减1,直到state等于0则表示完全释放锁成功 。
- head、tail:用于存放获得锁失败的线程 。在AQS中,每一个线程会被封装成一个Node节点,这些节点如果获得锁资源失败会链在head、tail中,成为一个双向链表结构 。
- exclusiveOwnerThread:用于存放当前获得锁的线程,正如在state说明的那样 。ReentrantLock判断可重入的条件就是用这个exclusiveOwnerThread线程跟申请获得锁的线程做比较,如果是同一个线程,则state+1,并重入加锁成功 。
public final class MySync extends AbstractQueuedSynchronizer {/*** 尝试加锁*/@Overrideprotected boolean tryAcquire(int arg) {if (compareAndSetState(0, 1)) {// 修改state状态成功后设置当前线程为占有锁资源线程setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}/*** 释放锁*/@Overrideprotected boolean tryRelease(int arg) {setExclusiveOwnerThread(null);// state有volatile修饰,为了保证解锁后其他的一些变量对其他线程可见,把setExclusiveOwnerThread(null)放到上面 hAppens-before中定义的 volatile规则setState(0);return true;}/*** 判断是否是独占锁*/@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}}复制代码将MySync组合进MyLock最后一步就是将第一步中的所有方法逻辑完成class MyLock implements Lock {// 组合自定义sync器private MySync sync = new MySync();/*** 加锁 。如果不成功则进入等待队列*/public void lock() {sync.acquire(1);}/*** 加锁(可被interrupt)*/public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}/*** 尝试加锁*/public boolean tryLock() {return sync.tryAcquire(1);}/*** 加锁 带超时的*/public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toMillis(time));}/*** 释放锁*/public void unlock() {sync.release(0);}/*** 返回一个条件变量(不在本案例谈论)*/@Overridepublic Condition newCondition() {return null;}}复制代码完成整个MyLock的逻辑后,发现在lock()、unlock()中调用的自定义sync的方法tryAcquire()和tryRelease()方法 。我们就以在lock()方法中调用acquire()方法说明模板设计模式在AQS中的应用 。点进.acquire()方法后,发现该该方法是来自
AbstractQueuedSynchronizer中:

文章插图
- 在这里面可以看到tryAcquire方法,继续点进去看看tryAcquire(),发现该方法是一个必须被重写的方法,否则抛出一个运行时异常 。
- 模板方法设计模式在这里得以体现,再回到我们第二部中自定义的MySync中,就是重写了AQS中的tryAcquire()方法 。
推荐阅读
- Java循环结构——switch语句
- Javascript 中New 操作符 解读
- 浏览器的工作原理是怎样的?是如何把网页显示出来的?
- 不仅限于Java 我们必须要了解的Java位运算
- 长期淡斑美白喝什么茶,玫瑰花茶和什么搭配祛斑美白
- 重学SpringMVC:框架原理解读 + 简单入门程序+组件分析.
- Google 关键字的「搜寻量」收录指数:如何使用这个数据指标?
- 为什么我们总是推荐Java?Java为什么值得学?
- 学Java还是前端?我是这么看的
- Java开发环境搭建与配置,最全手册看这一篇就够了
