并行模式之Guarded Suspension模式

一)、Guarded Suspension: 保护暂存模式

应用场景:当多个客户进程去请求服务进程时,客户进程的请求速度比服务进程处里请求的速度快,此时,为了保护客户进程的请求不会丢失,使用 Guarded Suspension模式,增加一个请求队列,客户进程和服务进程共同维护这一个队列。

好处:

1).将客户进程的请求放入到请求队列中,当服务进程有能力处理客户请求时取出请求队列中的客户请求,并处理。

2).确保系统仅在有能力处理某个任务时,它才处理该任务,当系统没有能力处理任务时,它将暂存任务信息,等待系统空闲。

二)、Guarded Suspension的结构

1).请求队列(RequestQueue)

    作用:内部结构是一个链表集合LinkedList, 提供了暂缓客户请求,和取出客户请求的方法,客户进程和服务进程共同维护一个请求队列。
  1. .客户进程(ClientThread)

    作用: 发送请求,将请求存入都请求列表中。

3).服务进程(ServiceThread)

   作用:处理请求,对请求队列中的请求进行处理。

三)、Guarded Suspension的简单实现

Request类: 模拟请求的数据

/**
* Guarded Suspension : 保护暂存模式。
* 应用场景:
* 当多个客户端去请求服务端应用程序,服务端无法快速的处理客户端的请求,此时,需要保存客户端的请求,将客户端的请求保存
* 在一个缓存队列中,等待服务端处理。
*
* 结构: 由服务线程、客户线程、请求队列(用来存放客户的请求)构成,服务线程和客户线程共同维护一个请求队列。
*
*/ /**
* 构造一个请求对象
*/
public class Request {
private String name;
public Request(String name){
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString(){
return name;
}
}

RequestQueue类:请求队列

/**
* 请求队列:
* 属性:queue,用于存储客户请求的链表集合
* 方法:getRequest(),获取链表中的请求
* addRequest(),添加客户情求
*/
public class RequestQueue {
/**
* 内部结构:链表集合,充当存储请求的缓冲区
*/
protected LinkedList queue = new LinkedList(); /**
* 获取用户的请求
*/
public synchronized Request getRequest(){
//当服务端获取请求时,先判断队列中是否有待处理的请求,没有则让线程进入等待状态
if(queue.size() == 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//队列中有请求则通过remove()中获取请求,并移除,使用remove()默认移除链表第一个元素,并返回移除的值,
//当链表中没有元素,调用remove()会抛出NoSerachElementException
return (Request) queue.remove();
}
/**
* 添加客户的请求
*/
public synchronized void addRequest(Request request){
queue.add(request);
//唤醒等待的服务线程
notifyAll();
}
}

ClientThread: 客户进程

/**
* 客户线程
*/
public class ClientThread implements Runnable{
/**
* 内部维护一个请求队列
*/
protected RequestQueue requestQueue = new RequestQueue();
public ClientThread(RequestQueue requestQueue){
this.requestQueue = requestQueue;
} @Override
public void run() {
//构造客户请求
for(int i = 0; i < 10; i++){
Request request = new Request("RequestId:"+ i + " Thread_Name:" + Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+" "+"request:"+request);
//将请求放入请求队列
requestQueue.addRequest(request);
//控制客户端提交请求的速度,快于服务端处理请求的速度
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ClientThread Name is"+ Thread.currentThread().getName()+" "+"request:"+request);
}
System.out.println(Thread.currentThread().getName()+" end");
}
}

ServiceThread: 服务进程

/**
* 服务端线程
*
*/
public class ServiceThread implements Runnable{
/**
* 内部维护一个请求队列
*/
protected RequestQueue requestQueue = new RequestQueue();
public ServiceThread(RequestQueue requestQueue){
this.requestQueue = requestQueue;
} /**
* 获取请求,处理请求
*/
@Override
public void run() {
while(true) {
final Request request = requestQueue.getRequest();
//模拟请求处理耗时,控制请求处理的时间比请求发送时间耗时长
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} //模拟请求被处理
System.out.println(Thread.currentThread().getName()+" " + "handles:" + request);
}
} }

ClientService: 启动类

/**
* 模拟客户端发送请求,服务端处理请求
*/
public class ClientAndService {
public static void main(String[] args) {
//客户端和服务端共享的请求队列
RequestQueue queue = new RequestQueue();
//客户端发送请求
ClientThread clientThread = new ClientThread(queue);
for(int i = 0; i < 10; i++){
new Thread(clientThread,"ClientThread"+i).start();
}
//服务端处理请求
ServiceThread serviceThread = new ServiceThread(queue);
for(int i = 0; i < 10; i++){
new Thread(serviceThread,"ServiceThread"+i).start();
}
//结论:服务端处理请求的速度比发送请求慢,因为加入了缓存的队列,当客户端结束发送请求,服务端依然可以从缓存队列中取出客户的
//请求并处理,保证了客户的请求不会丢失。
}
}

运行结果:

注:因为运行结果过长,以下给出的是部分的运行结果。

ServiceThread5 handles:RequestId:0 Thread_Name:ClientThread5
ServiceThread4 handles:RequestId:0 Thread_Name:ClientThread4
ServiceThread3 handles:RequestId:0 Thread_Name:ClientThread1
ClientThread Name isClientThread4 request:RequestId:9 Thread_Name:ClientThread4
ClientThread Name isClientThread2 request:RequestId:9 Thread_Name:ClientThread2
ClientThread2 end
ClientThread Name isClientThread0 request:RequestId:9 Thread_Name:ClientThread0
ClientThread0 end
ClientThread Name isClientThread5 request:RequestId:9 Thread_Name:ClientThread5
ClientThread5 end
ClientThread4 end
ClientThread Name isClientThread8 request:RequestId:9 Thread_Name:ClientThread8
ClientThread8 end
ClientThread Name isClientThread3 request:RequestId:9 Thread_Name:ClientThread3
ClientThread3 end
ClientThread Name isClientThread7 request:RequestId:9 Thread_Name:ClientThread7
ClientThread7 end
ClientThread Name isClientThread9 request:RequestId:9 Thread_Name:ClientThread9
ClientThread9 end
ClientThread Name isClientThread6 request:RequestId:9 Thread_Name:ClientThread6
ClientThread6 end
ClientThread Name isClientThread1 request:RequestId:9 Thread_Name:ClientThread1
ClientThread1 end
ServiceThread1 handles:RequestId:1 Thread_Name:ClientThread1
ServiceThread0 handles:RequestId:1 Thread_Name:ClientThread4
ServiceThread2 handles:RequestId:1 Thread_Name:ClientThread5

结论: 当客户进程停止发送请求时,服务进程仍在继续工作,这说明

          RequestQueue发挥了中间缓存的作用,客户端的请求没有丢失。

四)、携带返回结果的Guarded Suspension(+Future 模式)

1)、Guarded Suspension的弊端:

客户进程的Request不能获取服务进程的返回结果,当客户进程去请求服务进程必须使用服务进程的返回值,客户进程不知道服务进程何时处理请求,也不知道需要处理多久,因此,可以使用Future模式,在客户请求时返回一个虚假的数据FutureData对象,此时,客户进程可以先处理其它的业务逻辑,当服务真正的处理完请求,再返回真实的处理数据。


2)、Guarded Suspension(+Future 模式)的结构

1.Request: 请求类

作用: 接收请求信息,内部维护了一个数据返回结果接口Data。

2.Data: 接口

作用: FutureData 和 RealData的共同接口,定义了一个获取数据返回结果的方法。

3.FutureData: 虚假数据类

作用:轻量级对象,实现了Data接口,内部维护了一个RealData对象,在请求处理时注入, 是RealData的一个代理类,是客户进程发送请求后立即返回的一个虚假数据类。

4.RealData: 真实数据类

作用: 真实数据类,做获取数据的真实操作

5.RequestQueue: 请求队列

作用:内部结构是一个链表集合LinkedList, 提供了暂缓客户请求,和取出客户请求的方法,客户进程和服务进程共同维护一个请求队列。

6.ClienThread: 用户进程

作用:发送请求并获取请求的数据。

7.ServiceThread

作用:处理请求。

五)、Guarded Suspension(+Future 模式)的简单实现

Request类:请求类

/**
* 构造请求类
* 属性:
* name: 请求信息
* Data: 返回给客户端的处理结果
*
*/
public class Request {
/**
* 请求信息
*/
private String name; /**
* 响应信息
*/
private Data response; public Request(String name) {
this.name = name;
} @Override
public String toString(){
return name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Data getResponse() {
return response;
} public void setResponse(Data response) {
this.response = response;
}
}

Data: 数据结果接口

/**
获取数据结果的方法
*/
public interface Data {
String getResult();
}

FutureData: 虚假数据类

/**
* 虚假数据类: 轻量级对象
*/
public class FutureData implements Data {
/**
* 内部维护一个RealData
*/
protected RealData realData; /**
* 使用一个变量来监控是否成功注入RealData
*/
private boolean isReady = false; /**
* 注入真实的RealData
*/
public synchronized void setRealData(RealData realData){
if(isReady){
return;
}
this.realData = realData;
//注入成功,将isReady值设为true,唤醒等待的线程
isReady = true;
notifyAll();
} /**
* 获取真实的返回数据
*/
@Override
public synchronized String getResult() {
//先判断RealData对象是否注入成功,若没有则让线程进入等待桩状态
if(!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//注入成功,返回真实的处理结果
return realData.getResult();
}
}

RealData: 真实数据类

/**
* 真实的数据类: 是一个重量级对象
*/
public class RealData implements Data {
/**
* 请求信息
*/
private Request request;
/**
* 模拟构造方法构造很慢
*/
public RealData(Request request){
this.request = request;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String getResult() {
return "我处理了 "+request;
}
}

RequestQueue: 请求队列

/**
* 请求队列: ServiceThread和ClientThread共同维护的对象
* 属性:
* LinkdList: 存储客户端的请求
* 方法:
* addRequest(): 添加请求
* getRequest(): 获取请求
*/
public class RequestQueue {
/**
* 请求队列,
*/
protected LinkedList requestQueue = new LinkedList(); /**
* 获取请求
*/
public synchronized Request getRequest(){
//获取请求时先判断队列中是否有请求,若没有让线程进入当待状态
if(requestQueue.size() == 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有请求,返回链表第一个元素,并移除,remove()默认移除链表第一个元素,并返回移除值
//当链表没有元素时,调用remove()方法会抛NoSearchElementException
return (Request) requestQueue.remove();
} /**
* 添加请求
*/
public synchronized void addRequest(Request request){
requestQueue.add(request);
//当用户添加请求后,队列存在数据,唤醒服务端的等待线程
notifyAll();
}
}

ClientThread: 客户进程

**
* 客户线程
* 属性: RequestQueue,请求队列,将请求放入队列中
*/
public class ClientThread extends Thread{
/**
* 请求队列
*/
protected RequestQueue requestQueue; public ClientThread(RequestQueue requestQueue, String name) {
super(name);
this.requestQueue = requestQueue;
}
@Override
public void run(){
for(int i = 0; i < 10; i++) {
//构造request,模拟发送的请求
Request request = new Request("[RequestId:" + i + " Thread_Name:" + Thread.currentThread().getName());
//将请求放入请求队列
requestQueue.addRequest(request);
//用户发送完请求变可以获得一个轻量级的响应对象
request.setResponse(new FutureData());
//模拟用户不用等待真实数据的返回,处理其他业务逻辑
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取真实的响应数据
System.out.println(request.getResponse().getResult());
System.out.println(Thread.currentThread().getName()+" "+request);
//模拟用户发送请求的时间比服务处理请求的快
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ClientThread name is "+Thread.currentThread().getName());
}
System.out.println(Thread.currentThread().getName()+" is end!");
}
}

ServiceThread: 服务进程

/**
* 服务进程:
* 属性:RequestQueue,获取请求的队列
*/
public class ServiceThread extends Thread{
/**
* 请求队列
*/
protected RequestQueue requestQueue; public ServiceThread(RequestQueue requestQueue, String name) {
super(name);
this.requestQueue = requestQueue;
} @Override
public void run(){
while(true){
//从队列中获取请求
Request request = requestQueue.getRequest();
//进行真正的数据处理并返回
FutureData future = (FutureData) request.getResponse();
RealData realData = new RealData(request);
future.setRealData(realData);
//模拟用户对请求进行了处理
System.out.println(Thread.currentThread().getName()+" handle"+request);
//模拟用户处理请求的时间长于发送请求的时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }

ClientService: 启动类

/**
* 客户端发送请求,服务端处理请求
*/
public class ClientService {
public static void main(String[] args) {
//客户进程和服务进程共同维护一个请求队列
RequestQueue requestQueue = new RequestQueue();
//客户端请求
for(int i = 0; i < 10; i++){
new ClientThread(requestQueue,"ClientThread"+i).start();
}
//服务端请求
for(int i = 0; i < 10; i++){
new ServiceThread(requestQueue, "ServiceThread"+ i).start();
}
}
}

运行结果:

并行模式之Guarded Suspension模式的更多相关文章

  1. 多线程程序设计学习(4)guarded suspension模式

    Guarded Suspension[生产消费者模式] 一:guarded suspension的参与者--->guardedObject(被防卫)参与者                1.1该 ...

  2. 多线程同步循环打印和Guarded suspension 模式

     * 迅雷笔试题: * 有三个线程ID分别是A.B.C,请有多线编程实现,在屏幕上循环打印10次ABCABC…  由于线程执行的不确定性,要保证这样有序的输出,必须控制好多线程的同步. 线程同步有两种 ...

  3. 多线程系列之四:Guarded Suspension 模式

    一,什么是Guarded Suspension模式如果执行现在的处理会造成问题,就让执行处理的线程等待.这种模式通过让线程等待来保证实例的安全性 二,实现一个简单的线程间通信的例子 一个线程(Clie ...

  4. 并发设计模式之Guarded Suspension模式

    - 原文链接: http://www.joyhwong.com/2016/11/19/并发设计模式之guarded-suspension模式/ Guarded Suspension意为保护暂停,其核心 ...

  5. Guarded Suspension模式简单实现

    Guarded Suspension 意为保护暂停,假设服务器很短时间内承受大量的客户端请求,客户端请求的数量超过服务器本身的即时处理能力,而服务器又不能丢弃任何一个客户端请求,此时可以让客户端的请求 ...

  6. 多线程学习之三生产者消费者模式Guarded Suspension

    Guarded Suspension[生产消费者模式] 一:guarded suspension的参与者--->guardedObject(被防卫)参与者                1.1该 ...

  7. Guarded Suspension Pattern【其他模式】

    Guarded Suspension Pattern public class GuardedSuspension { /** * Guarded Suspension Pattern[保护悬挂模式] ...

  8. MySQL+MGR 单主模式和多主模式的集群环境 - 部署手册 (Centos7.5)

    MySQL Group Replication(简称MGR)是MySQL官方于2016年12月推出的一个全新的高可用与高扩展的解决方案.MGR是MySQL官方在5.7.17版本引进的一个数据库高可用与 ...

  9. 实验:keepalived双主抢占模式和非抢占模式和IPVS

    内容: 一:概念.原理   二:实验过程 一.概念 一.keepalived原理及配置解析 keepalived:vrrp协议的实现 vrrp协议:virtual router redundancy ...

随机推荐

  1. 5. Sersync实时同步

    rsync+Sersync数据的实时同步 sersync介绍 1.什么是实时同步 监控一个目录的变化, 当该目录触发事件(创建\删除\修改) 就执行动作, 这个动作可以是 rsync同步 ,也可以是其 ...

  2. python函数与异常处理

    一.python函数 1.函数自定义格式: 分为有无返回值两种类型 def 函数名(): 代码语句 -------- -------- return 参数1,(参数2等)--------------- ...

  3. echarts画中国地图,省市区地图分享

    中国地图 四川地图 重庆地图 源码分享: https://github.com/livelyPeng/ec-map

  4. 4. SOFAJRaft源码分析— RheaKV初始化做了什么?

    前言 由于RheaKV要讲起来篇幅比较长,所以这里分成几个章节来讲,这一章讲一讲RheaKV初始化做了什么? 我们先来给个例子,我们从例子来讲: public static void main(fin ...

  5. C#关于private protected sealed Virtual/Override

    Public:公开权限 Private:修饰类时类为程序集或者包含此类的类内部权限:修饰变量时只能类内部使用: Protected:修饰变量,只能继承类可以使用,对外(包括继承类的实例)无权限: Ab ...

  6. UE4蓝图与C++交互——射击游戏中多武器系统的实现

    回顾   学习UE4已有近2周的时间,跟着数天学院"UE4游戏开发"课程的学习,已经完成了UE4蓝图方面比较基础性的学习.通过UE4蓝图的开发,我实现了类似CS的单人版射击游戏,效 ...

  7. 爬虫基本库的使用---urllib库

    使用urllib---Python内置的HTTP请求模块 urllib包含模块:request模块.error模块.parse模块.robotparser模块 发送请求 使用 urllib 的 req ...

  8. Luogu P1816 忠诚

    rmq模板题.用st表切一个. 关于st表的详解见我的博客:st表.树状数组与线段树 笔记与思路整理 题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家 ...

  9. 一个自动管理项目的Makefile(C语言)

    Linux 是所有嵌入式软件工程师绕不过去的坎, makefile 是在Linux系统中绕不过去的坎. 花了几天时间初步学习和了解了makefile 的作用以及功能,并且制作了一个通用型的makefi ...

  10. Pandas 计算工具介绍

    # 导入相关库 import numpy as np import pandas as pd 统计函数 最常见的计算工具莫过于一些统计函数了.首先构建一个包含了用户年龄与收入的 DataFrame i ...