在前一节实现异步调用的基础上 , 现在我们来看一下一个完善的 Java 异步消息处理机制 .
[ 写在本节之前 ]
在所有这些地方 , 我始终没有提到设计模式这个词 , 而事实上 , 多线程编程几乎每一步都在应该设计模式 . 你只要能恰如其份地应用它 , 为什么要在意你用了某某名称的模式呢 ?
一个说书人它可以把武功招数说得天花乱坠 , 引得一班听书客掌声如雷 , 但他只是说书的 . 真正的武林高手也许并不知道自己的招式在说书人口中叫什么 , 这不重要 , 重要的是他能在最恰当的时机把他不知名的招式发挥到极致 !
你了解再多的设计模式 , 或你写出了此类的著作 , 并不重要 , 重要的是你能应用它设计出性能卓越的系统 .
本节的这个例子 , 如果你真正的理解了 , 不要怀疑自己 , 你已经是 Java 高手的行列了 . 如果你抛开本节的内容 , 五天后能自己独立地把它再实现一次 , 那你完全可以不用再看我写的文章系列了 , 至少是目前 , 我再也没有更高级的内容要介绍了 .
上节的 Java 异步调用为了简化调用关系 , 很多角色被合并到一个类中实现 , 为了帮助大家改快地抓住核心的流程 . 那么一个真正的异步消息处理器 , 当然不是这样的简单 .
一. 它要能适应不同类型的请求 :
本节用 makeString 来说明要求有返回值的请求 . 用 displayString 来说明不需要返回值的请求 .
二. 要能同时并发处理多个请求 , 并能按一定机制调度 :
本节将用一个队列来存放请求 , 所以只能按 FIFO 机制调度 , 你可以改用 LinkedList, 就可以简单实现一个优先级 ( 优先级高的 addFirst, 低的 addLast).
三. 有能力将调用的边界从线程扩展到机器间 (RMI)
四. 分离过度耦合 , 如分离调用句柄 ( 取货凭证 ) 和真实数据的实现 . 分离调用和执行的过程 , 可以尽快地将调返回 .
现在看具体的实现 :
public interface Axman {
Result resultTest(int count,char c);
void noResultTest(String str);
}
这个接口有两个方法要实现 , 就是有返回值的调用 resultTest 和不需要返回值的调用
noResultTest, 我们把这个接口用一个代理类来实现 , 目的是将方法调用转化为对象 , 这样就可以将多个请求 ( 多个方法调 ) 放到一个容器中缓存起来 , 然后统一处理 , 因为 Java 不支持方法指针 , 所以把方法调用转换为对象 , 然后在这个对象上统一执行它们的方法 , 不仅可以做到异步处理 , 而且可以将代表方法调用的请求对象序列化后通过网络传递到另一个机器上执行 (RMI). 这也是 Java 回调机制最有力的实现 .
一个简单的例子 .
如果 1: 做 A
如果 2: 做 B
如果 3: 做 C
如果有 1000 个情况 , 你不至于用 1000 个 case 吧 ? 以后再增加呢 ?
所以如果 C/C++ 程序员 , 会这样实现 : (c 和 c++ 定义结构不同 )
type define struct MyStruct{
int mark;
(*fn) ();
} MyList;
然后你可以声明这个结构数据 :
{1,A,
2,B
3,C
}
做一个循环 :
for(i=0;i<length;i++) {
if( 数据组 [i].mark == 传入的值 ) ( 数据组 [i].*fn)();
}
简单说 c/c++ 中将要被调用的涵数可以被保存起来 , 然后去访问 , 调用 , 而 Java 中 , 我们无法将一个方法保存 , 除了直接调用 , 所以将要调用的方法用子类来实现 , 然后把这些子类实例保存起来 , 然后在这些子类的实现上调用方法 :
interface My{
void test();
}
class A implements My{
public void test(){
System.out.println(“A”):
}
}
class B implements My{
public void test(){
System.out.println(“B”):
}
}
class C implements My{
public void test(){
System.out.println(“C”):
}
}
class MyStruct {
int mark;
My m;
public MyStruct(int mark,My m){this.mark = amrk;this.m = m}
}
数组 :
{ new MyStruct(1,new A()),new MyStruct(2,new B()),new MyStruct(3,new C())}
for(xxxxxxxxx) if( 参数 == 数组 [i].mark) 数组 [i].m.test();
这样把要调用的方法转换为对象的保程不仅仅是可以对要调用的方法进行调度 , 而且可以把对象序列化后在另一台机器上执行 , 这样就把调用边界从线程扩展到了机器 .
回到我们的例子 :
class Proxy implements Axman{
private final Scheduler scheduler;
private final Servant servant;
public Proxy(Scheduler scheduler,Servant servant){
this.scheduler = scheduler;
this.servant = servant;
}
public Result resultTest(int count,char c){
FutureResult futrue = new FutureResult();
this.scheduler.invoke(new ResultRequest(servant,futrue,count,c));
return futrue;
}
public void noResultTest(String str){
this.scheduler.invoke(new NoResultRequest(this.servant,str));
}
}
其中 scheduler 是管理对调用的调度 , servant 是真正的对方法的执行 :
Servant 就是去真实地实现方法 :
class Servant implements Axman{
public Result resultTest(int count,char c){
char[] buf = new char[count];
for(int i = 0;i < count;i++){
buf[i] = c;
try{
Thread.sleep(100);
}catch(Throwable t){}
}
return new RealResult(new String(buf));
}
public void noResultTest(String str){
try{
System.out.println("displayString :" + str);
Thread.sleep(10);
}catch(Throwable t){}
}
}
在 scheduler 将方法的调用 (invkoe) 和执行 (execute) 进行了分离 , 调用就是开始 ” 注册 ” 方法到要执行的容器中 , 这样就可以立即返回出来 . 真正执行多久就是 execute 的事了 , 就象一个人点燃爆竹的引信就跑了 , 至于那个爆竹什么时候爆炸就不是他能控制的了 .
public class Scheduler extends Thread {
private final ActivationQueue queue;
public Scheduler(ActivationQueue queue){
this.queue = queue;
}
public void invoke(MethodRequest request){
this.queue.putRequest(request);
}
public void run(){
while(true){
// 如果队列中有请求线程 , 测开始执行请求
MethodRequest request = this.queue.takeRequest();
request.execute();
}
}
}
在 schetbduler 中只用一个队列来保存代表方法和请求对象 , 实行简单的 FIFO 调用 , 你要实更复杂的调度就要在这里重新实现 :
class ActivationQueue{
private static final int MAX_METHOD_REQUEST = 100;
private final MethodRequest[] requestQueue;
private int tail;
private int head;
private int count;
public ActivationQueue(){
this.requestQueue = new MethodRequest[MAX_METHOD_REQUEST];
this.head = this.count = this.tail = 0;
}
public synchronized void putRequest(MethodRequest request){
while(this.count >= this.requestQueue.length){
try {
this.wait();
}
catch (Throwable t) {}
}
this.requestQueue[this.tail] = request;
tail = (tail + 1)%this.requestQueue.length;
count ++ ;
this.notifyAll();
}
public synchronized MethodRequest takeRequest(){
while(this.count <= 0){
try {
this.wait();
}
catch (Throwable t) {}
}
MethodRequest request = this.requestQueue[this.head];
this.head = (this.head + 1) % this.requestQueue.length;
count --;
this.notifyAll();
return request;
}
}
为了将方法调用转化为对象 , 我们通过实现 MethodRequestb 对象的 execute 方法来方法具体方法转换成具体对象 :
abstract class MethodRequest{
protected final Servant servant;
}