「Java原理探索」「AQS」教你自定义实现自己的同步器

前提概要

之前的文章中会涉及到了相关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中主要的一些成员属性如下:
「Java原理探索」「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,并重入加锁成功 。
知道这些概念后我们就可以自定义一个AQS:
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中:
「Java原理探索」「AQS」教你自定义实现自己的同步器

文章插图