今天要说的模式是前几个模式的综合体,也是这个系列的最后的一个模式,叫做接受异步消息的主动对象。与之相关的模式有java多线程设计模式之Future Pattern、 java多线程设计模式之消费者生产者模式、 java多线程设计模式之Guarded Suspension等,如果不了解这几个模式,最好先看下这几个模式。
何为主动对象,一般来说是指自己拥有独立的线程的对象,在这里不只是拥有独立线程,还可从外部接受异步消息,并能配合需要返回处理结果。代码是最好的老师,上代码:
首先是主动对象接口,为什么要接口呢,因为这里使用了代理模式:
public interface ActiveObject {
Result makeString(int count,char fillChar);
void displayString(String string);
}
我们的主动对象:
public class Servant implements ActiveObject{
@Override
public Result makeString(int count, char fillChar) {
char[] buffer = new char[count];
for (int i = 0; i < count; i++) {
buffer[i] = fillChar;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return new RealResult(new String(buffer));
}
@Override
public void displayString(String string) {
System.out.println("display string"+string);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
两个简单的方法,一个生成一个字符串传入RealResult类对象,一个现实一个字符串。
RealResult相信看过FuturePattern就知道是什么意思。至于RealResult代码是什么,稍后会说。
主动对象的代理类,持有主动对象的引用,被客户端实际调用的类,也是主动对象和调度线程SchedulerThread的中介者。至于调度线程,也是稍后再谈。
public class Proxy implements ActiveObject {
private final SchedulerThread schedulerThread;
private final Servant servant;
public Proxy(SchedulerThread schedulerThread, Servant servant) {
this.schedulerThread = schedulerThread;
this.servant = servant;
}
public Result makeString(int count,char fillchar){
FutureResult futureResult = new FutureResult();
schedulerThread.invoke(new MakeStringRequest(servant,futureResult,count,fillchar));
return futureResult;
}
public void displayString(String string){
schedulerThread.invoke(new DisplayStringRequest(servant,string));
}
}
这里Proxy持有Servant和SchedulerThread的引用,在makeString的时候将一个FutureResult(作用和Future Pattenr中的 FutureResult一样)的对象那传入一个MakeStringRequest的对象,然后 SchedulerThread的invoke方法接受MakeStringRequest的对象作为参数。
主动对象的工厂类:
public class ActiveObjectFactory {
public static ActiveObject createActiveObject(){
Servant servant = new Servant();
ActivationQueue queue = new ActivationQueue();
SchedulerThread schedulerThread = new SchedulerThread(queue);
Proxy proxy = new Proxy(schedulerThread,servant);
schedulerThread.start();
return proxy;
}
}
该类直接和客户端打交道,客户端使用主动对象都是从该类获取,并且返回的是ActiveObject接口,实现了解耦。
调度线程:
public class SchedulerThread extends Thread{
private ActivationQueue activationQueue;
public SchedulerThread(ActivationQueue activationQueue) {
this.activationQueue = activationQueue;
}
public void invoke(MethodRequest methodRequest){
activationQueue.putRequest(methodRequest);
}
@Override
public void run() {
while (true){
MethodRequest methodRequest = activationQueue.takeRequest();
methodRequest.execute();
}
}
}
如果看过生产着消费者模式就会觉得很简单,持有一个ActiveationQueue对象(看名字就知道是请求队列),然后invoke方法就是将一个MethodeRequest放入队列,而本身开启的线程则是循环从队列中取出请求执行请求的execute方法。
结合上面的Proxy就能看出,Proxy主要就是将MakeStringRequest和DiaplayStringRequest放入ActiveationQueue对象。
请求队列ActiveationQueue:
public class ActivationQueue {
private static final int MAX_REQUEST = 100;
private MethodRequest[] methodRequests;
//下一个要放的位置
private int tail;
//下一个要取的位置
private int head;
//蛋糕数量
private int count;
public ActivationQueue() {
this.methodRequests = methodRequests = new MethodRequest[MAX_REQUEST];
tail = 0;
head = 0;
count = 0;
}
public synchronized void putRequest(MethodRequest methodRequest){
while (count >= methodRequests.length){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
methodRequests[tail] = methodRequest;
tail = (tail+1)%methodRequests.length;
count++;
notifyAll();
}
public synchronized MethodRequest takeRequest() {
while (count <= 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MethodRequest methodRequest = methodRequests[head];
head = (head+1)%methodRequests.length;
count--;
notifyAll();
return methodRequest;
}
}
一个很普通的阻塞队列,对生产着消费者模式不熟悉的可以看java多线程设计模式之消费者生产者模式,这是一个很重要的模式。
接下来谈下请求积基类MethodRequest,也就是异步消息。在上面的SchedulerThread的invoke方法,是将一个方法调用转化为一个异步消息,让另一个线程去执行,这也是一种很重要的设计思想。
public abstract class MethodRequest {
protected final Servant servant;
protected final FutureResult futureResult;
public MethodRequest(Servant servant, FutureResult futureResult) {
this.servant = servant;
this.futureResult = futureResult;
}
public abstract void execute();
}
具体的请求:
public class MakeStringRequest extends MethodRequest{
private final int count;
private final char fillChar;
public MakeStringRequest(Servant servant, FutureResult futureResult, int count, char fillChar) {
super(servant, futureResult);
this.count = count;
this.fillChar = fillChar;
}
@Override
public void execute() {
Result result = servant.makeString(count,fillChar);
futureResult.setResult(result);
}
}
public class DisplayStringRequest extends MethodRequest{
private final String string;
public DisplayStringRequest(Servant servant, String string) {
super(servant, null);
this.string = string;
}
@Override
public void execute() {
servant.displayString(string);
}
}
如果了解 java多线程设计模式之Future Pattern,那很容易理解,在Proxy的makeString方法中返回的是一个Result对象,实际为FutureResult,在这里可以看到FutureResult必须等到Servant的makeString方法(耗时操作)返回RealResult之后才可以持有RealResult对象的引用。
再看下Result:
public abstract class Result {
public abstract Object getResultValue();
}
ublic class FutureResult extends Result{
private Result result;
private boolean ready;
public synchronized void setResult(Result result){
this.result = result;
ready = true;
notifyAll();
}
public synchronized Object getResultValue(){
while (!ready){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result.getResultValue();
}
}
public class RealResult extends Result{
private final Object resultValue;
public RealResult(Object resultValue) {
this.resultValue = resultValue;
}
public Object getResultValue() {
return resultValue;
}
}
最后是客户端线程:
public class MakerClientThread extends Thread{
private final ActiveObject activeObject;
private final char fillchar;
public MakerClientThread(String name, ActiveObject activeObject) {
super(name);
this.activeObject = activeObject;
this.fillchar = name.charAt(0);
}
@Override
public void run() {
try {
for (int i = 0; true; i++) {
Result result = activeObject.makeString(i,fillchar);
Thread.sleep(10);
String value = (String) result.getResultValue();
System.out.println(Thread.currentThread().getName() + " getValue:" + value);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
制作字符串线程通过调用主动对象的makeString得到Result(FutureResult),等待一段时间后从Result中取结果。
public class DisplayClientThread extends Thread {
private final ActiveObject activeObject;
public DisplayClientThread(String name, ActiveObject activeObject) {
super(name);
this.activeObject = activeObject;
}
@Override
public void run() {
try {
for (int i = 0; true; i++) {
String string =Thread.currentThread().getName()+ " " + i;
activeObject.displayString(string);
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
循环调用主动对象的displayString方法。
Main:
public class Main1 {
public static void main(String args[]){
ActiveObject activeObject = ActiveObjectFactory.createActiveObject();
new MakerClientThread("A",activeObject).start();
new MakerClientThread("B",activeObject).start();
new DisplayClientThread("C",activeObject).start();
}
}
运行结果
display stringC 0
A getValue:
B getValue:
A getValue:A
B getValue:B
display stringC 1
A getValue:AA
B getValue:BB
display stringC 2
display stringC 3
A getValue:AAA
display stringC 4
B getValue:BBB
display stringC 5
A getValue:AAAA
display stringC 6:
..........
这个综合的模式中除了运用了多个多线程的模式外,也运用了面向对象解耦的设计思想,客户端线程和主动对象、调度线程和主动对象它们之间并没有直接的耦合。真正处理数据的只有Servant,但因为有耗时操作,所以放在新的线程SchedulerThread中,这也使得运行Servant的是单线程,所以即使发出请求的客户端线程有多个,也保证了Servant的线程安全。