Guarded Suspension模式的架构

核心是一个受保护方法(Guarded Method).该方法需要执行其所要真正执行的操作时需要满足特定的条件(Predicate,以下称之为保护条件)。当该条件不满足时,执行受保护方法的线程会被挂起进入等待状态,直到该条件满足时该线程才会继续运行。此时,受保护方法才会正在执行其所要执行的操作(目标操作)。
GuardedObject:包含受保护方法的对象。主要方法和职责如下:
-guardedMethod:受保护方法。
-stateChanged:改变GuardedObject实例状态的方法,该方法负责在保护条件成立时唤醒受保护方法的执行线程。
GuardedAction:抽象了目标动作,并关联了目标动作所需的保护条件。主要方法如下
-call:用于表示目标动作的方法。
ConcreteGuardedAction:应用程序所实现的具体目标动作及其关联的保护条件。
Predicate:抽象了保护条件
-evaluate:用于表示保护条件的方法。
ConcretePredicate:应用程序所实现的具体保护条件。
Blocker:负责对执行guardedMethod方法的线程进行挂起和唤醒,并执行ConcreteGuardedAction所实现的目标操作。主要方法如下:
callWithGuarded:负责执行目标操作和暂挂当前线程。
singleAfter:负责执行其参数指定的动作和唤醒改方法所属Blocker实例所暂挂的线程中的一个线程。
signal:负责唤醒由该方法所属Blocker实例所暂挂的线程中的一个线程。
broadcastAfter:负责执行其参数指定的动作和唤醒由该方法所属Blocker实例所暂挂的所有线程。
broadcast:负责唤醒由该方法所属Blocker实例暂挂的所有线程。
ConditionVarBlocker:基于java条件变量Condition实现的Blocker。


调用过程:
1.client调用受保护方法guardedMethod。
2.guardedMethod方法创建GuardedAction实例guardedAction.
3.guardedMethod方法以guardedAction为参数调用Blocker实例的callWithGuarded方法。
4-5:callWithGuarded方法调用guardedAction的getGuarded方法获取保护条件Predicate。
6-8这几个步骤是个循环。该循环判断守护条件是否成立。若保护条件成立,则该循环退出。否则,该循环会将当前线程暂挂使其处于等待状态。当其他线程唤醒该被暂挂的线程后,该循环任然继续监测保护条件并重复上述逻辑。
9-10:callWithGuarded方法调用guardedAction的call方法来执行目标操作,并记录call方法的返回值actionReturnValue。
11.callWithGuarded方法将actionReturnValue作为其返回值返回给调用方。
12.guaredMethod方法返回。

案例分析:系统告警功能模块
主要功能是将其接收到的告警信息发送给告警服务器。类AlarmAgent负责与告警服务器进行对接。sendAlarm方法负责通过网络连接将告警信息发送到告警服务器。AlarmAgent创建一个专门的线程用于其与告警服务器建立网络连接。因此,sendAlarm方法被调用的时候,连接线程可能还没有完成网络连接的建立。此时,snedAlarm方法应该等待连接线程建立好网络连接。另外,即便连接线程建立好了网络连接,中途也可能由于某些原因出现与告警服务器断连的情况。此时,sendAlarm方法需要等待心跳任务重新建立好连接才能上报告警信息。也就是说,snedAlarm方法必须在AlarmAgent与告警服务器的网络连接建立成功的情况下才能执行所需要执行的操作。若AlarmAgent与告警服务器的连接未建立(或者连接中断),sendAlarm方法的执行线程应该暂挂直到连接建立完毕(或者恢复)。

上述问题可以采用Guarded Suspension模式来解决。
import lombok.extern.slf4j.Slf4j;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
/**
* 负责连接告警服务器,并发送告警信息至告警服务器
*
*/
@Slf4j
public class AlarmAgent {
// 用于记录AlarmAgent是否连接上告警服务器
private volatile boolean connectedToServer = false;
public static void main(String[] args) {
AlarmAgent alarmAgent = new AlarmAgent();
alarmAgent.init();
}
// 模式角色:GuardedSuspension.Predicate
private final Predicate agentConnected = new Predicate() {
@Override
public boolean evaluate() {
return connectedToServer;
}
};
// 模式角色:GuardedSuspension.Blocker
private final Blocker blocker = new ConditionVarBlocker();
// 心跳定时器
private final Timer heartbeatTimer = new Timer(true);
// 省略其它代码
/**
* 发送告警信息
* @param alarm 告警信息
* @throws Exception
*/
public void sendAlarm(final AlarmInfo alarm) throws Exception {
// 可能需要等待,直到AlarmAgent连接上告警服务器(或者连接中断后重新连连上服务器)
// 模式角色:GuardedSuspension.GuardedAction
GuardedAction<Void> guardedAction = new GuardedAction<Void>(agentConnected) {
public Void call() throws Exception {
doSendAlarm(alarm);
return null;
}
};
blocker.callWithGuard(guardedAction);
}
// 通过网络连接将告警信息发送给告警服务器
private void doSendAlarm(AlarmInfo alarm) {
// 省略其它代码
log.info("sending alarm " + alarm);
// 模拟发送告警至服务器的耗时
try {
Thread.sleep(50);
} catch (Exception e) {
}
}
public void init() {
// 省略其它代码
// 告警连接线程
Thread connectingThread = new Thread(new ConnectingTask());
connectingThread.start();
heartbeatTimer.schedule(new HeartbeatTask(), 60000, 2000);
}
public void disconnect() {
// 省略其它代码
log.info("disconnected from alarm server.");
connectedToServer = false;
}
protected void onConnected() {
try {
blocker.signalAfter(new Callable<Boolean>() {
@Override
public Boolean call() {
connectedToServer = true;
log.info("connected to server");
return Boolean.TRUE;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
protected void onDisconnected() {
connectedToServer = false;
}
// 负责与告警服务器建立网络连接
private class ConnectingTask implements Runnable {
@Override
public void run() {
// 省略其它代码
// 模拟连接操作耗时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
;
}
onConnected();
}
}
/**
* 心跳定时任务:定时检查与告警服务器的连接是否正常,发现连接异常后自动重新连接
*/
private class HeartbeatTask extends TimerTask {
// 省略其它代码
@Override
public void run() {
// 省略其它代码
if (!testConnection()) {
onDisconnected();
reconnect();
}
}
private boolean testConnection() {
// 省略其它代码
return true;
}
private void reconnect() {
ConnectingTask connectingThread = new ConnectingTask();
// 直接在心跳定时器线程中执行
connectingThread.run();
}
}
}
Blocker接口:
import java.util.concurrent.Callable;

/**
* @author
* @create 2017 -09-30 下午1:15
*/
public interface Blocker {
/**
* 在保护条件成立时执行目标动作,否则阻塞当前线程,直到保护条件成立。
* @param guardedAction 带保护条件的目标动作
* @return
* @throws Exception
*/
<V> V callWithGuard(GuardedAction<V> guardedAction) throws Exception;
/**
* 执行stateOperation所指定的操作后,决定是否唤醒本Blocker
* 所暂挂的所有线程中的一个线程。
*
* @param stateOperation
* 更改状态的操作,其call方法的返回值为true时,该方法才会唤醒被暂挂的线程
*/
void signalAfter(Callable<Boolean> stateOperation) throws Exception;

void signal() throws InterruptedException;

/**
* 执行stateOperation所指定的操作后,决定是否唤醒本Blocker
* 所暂挂的所有线程。
*
* @param stateOperation
* 更改状态的操作,其call方法的返回值为true时,该方法才会唤醒被暂挂的线程
*/
void broadcastAfter(Callable<Boolean> stateOperation) throws Exception;
}

ConditionVarBlocker
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author
* @create 2017 -09-30 下午1:18
*/
@Slf4j
public class ConditionVarBlocker implements Blocker {
private final Lock lock;

private final Condition condition;

private final boolean allowAccess2Lock;

public ConditionVarBlocker(Lock lock) {
this(lock, true);
}

private ConditionVarBlocker(Lock lock, boolean allowAccess2Lock) {
this.lock = lock;
this.allowAccess2Lock = allowAccess2Lock;
this.condition = lock.newCondition();
}

public ConditionVarBlocker() {
this(false);
}

public ConditionVarBlocker(boolean allowAccess2Lock) {
this(new ReentrantLock(), allowAccess2Lock);
}

public Lock getLock() {
if (allowAccess2Lock) {
return this.lock;
}
throw new IllegalStateException("Access to the lock disallowed.");
}

public <V> V callWithGuard(GuardedAction<V> guardedAction) throws Exception {
//获得锁
lock.lockInterruptibly();
V result;
try {
//临界区代码
//获得保护条件
final Predicate guard = guardedAction.guard;
while (!guard.evaluate()) {
//保护条件不成立 线程挂起 暂挂ConditionVarBlocker实例
log.info("waiting...");
condition.await();
}
//执行目标动作
result = guardedAction.call();
return result;
} finally {
//释放锁 保证锁总是被释放的
lock.unlock();
}
}

public void signalAfter(Callable<Boolean> stateOperation) throws Exception {
lock.lockInterruptibly();
try {
if (stateOperation.call()) {
condition.signal();
}
} finally {
lock.unlock();
}

}

public void broadcastAfter(Callable<Boolean> stateOperation) throws Exception {
lock.lockInterruptibly();
try {
if (stateOperation.call()) {
condition.signalAll();
}
} finally {
lock.unlock();
}

}

public void signal() throws InterruptedException {
lock.lockInterruptibly();
try {
condition.signal();

} finally {
lock.unlock();
}

}
}
package com.credithc.finance.com.design.guardedmethod;

import java.util.concurrent.Callable;

/**
* @author
* @create 2017 -09-30 下午1:16
*/

public abstract class GuardedAction <V>implements Callable<V> {

protected final Predicate guard;

public GuardedAction(Predicate guard) {
this.guard = guard;
}
}
package com.credithc.finance.com.design.guardedmethod;

/**
* 保护条件判断
*
* @author
* @create 2017 -09-30 下午1:17
*/
public interface Predicate {
boolean evaluate();
}
线程Timer-0一直等待线程Thread-0所持有的锁(helper) 而线程Thread-0持有的helper一直未释放
可复用代码:Predicate,GuardedAction,Blocker,ConditionVarBlocker
需要自己实现的代码:GuaredObject ConcretePredicate ConcreteGuardedAction 这几个参与者实例

需要关注的问题

内存可见性和锁泄露
线程被过早唤醒
嵌套监视器锁死
可能增加JVM垃圾回收的负担

相关连接:http://blog.csdn.net/huzhiqiangCSDN/article/details/55045110

多线程编程-设计模式之保护性暂挂(Guarded Suspesion)模式的更多相关文章

  1. C++11 多线程编程 使用lambda创建std::thread (生产/消费者模式)

    要写个tcp server / client的博客,想着先写个c++11多线程程序.方便后面写博客使用. 目前c++11中写多线程已经很方便了,不用再像之前的pthread_create,c++11中 ...

  2. Java多线程编程实战指南 设计模式 读书笔记

    线程设计模式在按其有助于解决的多线程编程相关的问题可粗略分类如下. 不使用锁的情况下保证线程安全: Immutable Object(不可变对象)模式.Thread Specific Storage( ...

  3. java多线程编程模式

    前言 区别于java设计模式,下面介绍的是在多线程场景下,如何设计出合理的思路. 不可变对象模式 场景 1. 对象的变化频率不高 每一次变化就是一次深拷贝,会影响cpu以及gc,如果频繁操作会影响性能 ...

  4. 【Todo】【读书笔记】Java多线程编程指南-设计模式篇

    下了这本书<Java多线程编程指南-设计模式篇>, 还有另一本<JAVA多线程设计模式>,据说内容有重复,结合着看.

  5. 【转】Lua coroutine 不一样的多线程编程思路

    Lua coroutine 不一样的多线程编程思路 Sunday, Apr 26th, 2009 by Tim | Tags: coroutine, Lua 上周末开始看<Lua程序设计> ...

  6. Java多线程编程详解

    转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...

  7. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  8. java多线程12设计模式

    1.Single Threaded Execution Pattern(单线程运行模式) 2.Immutable Pattern(一成不变的模式) 3.Guarded Suspension Patte ...

  9. Java多线程编程核心技术

    Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...

随机推荐

  1. 【Elasticsearch】深入Elasticsearch集群

    7.1 节点发现启动Elasticsearch的时候,该节点会寻找有相同集群名字且课件的主节点,如果有加入,没有自己成为主节点,负责发现的模块两个目的 选出主节点以及发现集群的新节点7.1.1发现的类 ...

  2. [转]C# - JSON详解

    本文转自:http://www.cnblogs.com/QLJ1314/p/3862583.html 最近在做微信开发时用到了一些json的问题,就是把微信返回回来的一些json数据做一些处理,但是之 ...

  3. RestTemplate请求出现401错误

    最近遇到一个请求API接口总是报401 Unauthorized错误,起初是认为这个是平台返回的,后来用Postman请求,发现平台其实返回的是一串json,里面带有一些权限验证失败的消息,但到我们代 ...

  4. CSP学习之导出密钥BLOB 解析

    通过CryptExportKey( hKey, NULL, PUBLICKEYBLOB,0, NULL, &dwBlobLen) 函数导出的公钥信息如下: 06 02 00 00 00 A4 ...

  5. 微信小程序的button去边框

    wxml <button class='niu'>123123</button> css niu::after{ border:none; }

  6. 中值滤波C语言优化

    中值滤波C语言优化 图像平滑是图像预处理的基本操作,本文首先用不同的方法对一张图片做预处理比较它们效果的不同,然后针对中值滤波,实现了一种快速实现.(其实是copy的opencv实现,呵呵).因为op ...

  7. HBase伪分布式安装(HDFS)+ZooKeeper安装+HBase数据操作+HBase架构体系

    HBase1.2.2伪分布式安装(HDFS)+ZooKeeper-3.4.8安装配置+HBase表和数据操作+HBase的架构体系+单例安装,记录了在Ubuntu下对HBase1.2.2的实践操作,H ...

  8. dedecms 去掉栏目页的预览功能

    首先找到include/typeunit.class.admin.php 再找到 ListAllType 方法,该方法的功能是“读出所有分类” 找到并将该方法内的所以以下代码注释或者删除”<a ...

  9. 团队第三次scrum

    长大一条龙之课表查询 一.设计概要 本次内容主要是实现了长大一条龙系统的课表查询功能,我们的这个项目严格遵守MVC架构,采用前后端分离的策略.我们将课表查询分为二层,DAO层:负责与数据进行交互,读写 ...

  10. websocket常见错误

    当websockt连接是open的时候send()方法传送数据,当连接关闭或获取不到的时候回抛出异常. 一个通常的错误是人们喜欢在连接open之前发送消息.如下所示: // 这将不会工作 var ws ...