jdk遇到设计模式之模板方法
来源:互联网 发布:淘宝网毛衣货源 编辑:程序博客网 时间:2024/06/08 17:55
突然想写一个系列,记录在jdk中使用的设计模式。
1、jdk中有很多用到模板方法的地方,比如众所周知的当类comparable接口后可以用Collections.sort (和 Arrays.sort )。其实这就是一个经典的模板方法。
2、今天想详细的叙述另一个jdk中经典的模板方法的应用类AQS(AbstractQueuedSynchronizer),这是在java并发包中非常重要的一个类,比如ReetrantLock,ReadWriteLock等很多同步工具都是基于此类实现。甚至连jdk1.6之前的FutureTask都是基于此实现。(当然,由于通用和专用的天然矛盾,比如StampedLock就没有继承该抽象类)
根据记忆AQS有5个抽象方法(可能有误)需要子类去实现,
isHeldExclusively():该方法将返回同步对于当前线程是否是独占的(一般Condition需要去实现)
tryAcquire(int):独占方式。尝试获取资源。
tryRelease(int):独占方式。尝试释放资源。
tryAcquireShared(int):共享方式。尝试获取资源。
tryReleaseShared(int):共享方式。尝试释放资源。
今天的重点是模板方法,所以我们以tryAcquire(int)为例子来看看是AQS是如何应用模板方法的。
/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }在acquire方法中我们看到了调用tryAcquire,上面这段代码的大意是。
先尝试tryAcquire获取资源,失败的话
1、将当前等待线程包装成一个等待节点加入等待队列(addWaiter()方法)
2、从队列中尝试获取临界资源(acquireQueued()方法)
接下来看看addWaiter方法的实现
/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }试着把上面两个方法连起来看。
在addWaiter中先获取等待队列的尾节点,并且尝试设置新的尾节点。
cas失败或者tail是null的情况下,调用enq方法。
enq方法很简单,源码扫一眼就知道了。
通过上面的讲述,我们已经知道了addWaiter方法的实现。就是将等待节点插入等待队列中。
那么接下来看acquireQueued方法。
/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }在循环中主要做了这些事情:
1、找到当前节点的前任节点
2、前任节点是头节点、并且tryAcquire成功
3、把自己设置成头节点
4、前任节点不是头节点
5、睡、或者不睡这个取决于约定的时间(acquire(time))
6、睡得时候顺便检查一下是否被打断了
finally中主要是failed就取消Acquire。
最后看一下cancelAcquire方法吧
/** * Cancels an ongoing attempt to acquire. * * @param node the node */ private void cancelAcquire(Node node) { //node为空 if (node == null) return;
//将thread设置null
node.thread = null; // Skip cancelled predecessors Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; // predNext is the apparent node to unsplice. CASes below will // fail if not, in which case, we lost race vs another cancel // or signal, so no further action is necessary. Node predNext = pred.next; // Can use unconditional write instead of CAS here. // After this atomic step, other Nodes can skip past us. // Before, we are free of interference from other threads. node.waitStatus = Node.CANCELLED; // If we are the tail, remove ourselves. if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; // help GC } }这个douglea的注释已经很详尽了。
最后简单总结一下。按照个人的理解。模板方法就是在一个流程从具体的某一步骤需要由具体的情况决定。但是其他的步骤都是一致的。
所以可以定义抽象父类实现通用的步骤,由子类去实现专用的步骤。
- jdk遇到设计模式之模板方法
- jdk遇到设计模式之原型模式
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- 设计模式之模板方法
- [opencv编程]244版本跑231版本出错——fatal error LNK1104: 无法打开文件“opencv_core231d.lib”
- Gradle签名打包
- Ijkplayer Android介绍
- greendao3.2简单配置
- 51NOD 1605 棋盘问题 博弈
- jdk遇到设计模式之模板方法
- AndroidStudio关于jniLibs文件夹的个人理解
- [Leetcode] 151. Reverse Words in a String 解题报告
- 二阶段提交,三阶段提交,Paxos
- 复习
- Linux基础
- MVC5+Unity4.0注入依赖学习
- [bzoj2127] happiness 最小割
- 微信小程序开发—(十二)列表