java模板模式与AQS实现

本博客先简单看下模板模式,然后解析下java中的AQS是如何使用模板模式的,最后使用AQS自定义一种锁 。
模板模式
顾名思义,本模式旨在"套模板",跟写PPT时套模板道理一样,PPT模板事先给好布局、图片、配色等,用户添加自己的内容即可 。模板模式则是父类事先准备好一些函数框架,子类(用户类)继承父类并实现自己的功能即可 。UML图如下:
父类为虚类,其中方法伪代码如下:
【java模板模式与AQS实现】private void execute(){other logic.....methodOne();other logic.....other logic.....methodTwo();other logic.....methodThree();other logic.....}
即方法内容是固定的,子类不可复写,但是等方法是的,子类可以复写,这就达到一种目的:父类核心流程不能改,但是具体实现可以由子类实现 。
AQS
全称:(队列同步器),以下基于jdk12分析,某些方法已不同于java7、8
它是一种机制,用数字表示资源个数,用一个队列存储想获取资源的线程信息,并通过各种三种方式使线程竞争资源 。
这三种方式是
独占资源:一次仅能有一个线程访问资源共享资源:一次能有多个线程访问资源超时独占资源:与独占资源一样,只不过支持超时放弃
首先来看资源控制:
/*** The synchronization state.*/private volatile int state;
注释中说state为同步状态,AQS子类通过set和get这个变量控制同步状态,可以视为资源个数,下图可以看到方法被AQS子类调用 。为修饰,保证了内存可见性 。
存储线程信息的队列,这里称作同步队列吧,是一个双向链表,节点信息如下:
关键信息图中已标注,节点类型分为共享型和排他(独占)型,节点状态有、等,当然还有当前线程本身 。
下面分三种情况分析线程竞争情况:
情况一:独占资源
AQS中的模板方法为:
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
此方法定义了一种流程,子类不可复写,子类需要做的是复写方法,即获取资源(设置state变量)的方式,如下可以看到这个方法默认抛出异常,即子类要想实现独占锁,这个方法必须复写 。后文简单介绍可重入锁的实现,即基于此方法 。
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}
从方法看到,如果失败了,即没有当前线程没有获取到资源,会先调用方法,即向同步队列中添加一个包含当前线程的节点:
private Node addWaiter(Node mode) {Node node = new Node(mode);for (;;) {Node oldTail = tail;//1if (oldTail != null) {//2node.setPrevRelaxed(oldTail);//3if (compareAndSetTail(oldTail, node)) {oldTail.next = node;return node;}} else {initializeSyncQueue();}}}
可以看到是一个自旋添加的过程,tail为成员变量,即同步队列中的末尾,为修饰,只有通过CAS机制判断当前tail符合预期值时才会将新建的node添加到末尾 。这里的CAS是不断获取队列末尾,看这个末尾是否与经过123操作后的末尾是否一致,因为用的修饰,内存可见性得到保证,便防止了其他线程对tail的修改,CAS本质上是一种忙则等待的调度策略 。CAS一般称为,最终调用的相关方法,其实jdk12已经不这么叫了,这是jdk8及以前的叫法,jdk12称为,最终调用的相关方法,但是道理是一样的,都是拿内存中最新值与预期值对比,如果一致则赋予变量另一个新值 。