如何构建一个Java并发模型框架呢?让我们先回到原来的问题,先来分析一下原因。造成要维护多线程和单线程两个版本的原因是由于把应用逻辑和并发逻辑混在一起,如果能够做到把应用逻辑和并发模型进行很好的隔离,那么应用逻辑本身就可以很好的被复用,而且也很容易把并发逻辑添加进来而不会对应用逻辑造成任何影响。造成Client阻塞,性能降低以及无法进行额外的控制的原因是由于所有的服务调用都是同步的,解决方案很简单,改为异步调用方式,把服务的调用和服务的执行分离。
首先来介绍一个概念,活动对象(Active Object)。所谓活动对象是相对于被动对象(passive object)而言的,被动对象的方法的调用和执行都是在同一个线程中的,被动对象方法的调用是同步的、阻塞的,一般的对象都属于被动对象;主动对象的方法的调用和执行是分离的,主动对象有自己独立的执行线程,主动对象的方法的调用是由其他线程发起的,但是方法是在自己的线程中执行的,主动对象方法的调用是异步的,非阻塞的。
本框架的核心就是使用主动对象来封装并发逻辑,然后把Client的请求转发给实际的服务提供者(应用逻辑),这样无论是Client还是实际的服务提供者都不用关心并发的存在,不用考虑并发所带来的数据一致性问题。从而实现应用逻辑和并发逻辑的隔离,服务调用和服务执行的隔离。下面给出关键的实现细节。
本框架有如下几部分构成:
一个ActiveObject类,从Thread继承,封装了并发逻辑的活动对象
一个ActiveQueue类,主要用来存放调用者请求
一个MethodRequest接口,主要用来封装调用者的请求,Command设计模式的一种实现方式
它们的一个简单的实现如下:
package multithreading.module;
/**
*主要用来封装调用者的请求,Command设计模式的一种实现方式
*/
public interface MethodRequest {
public void call();
}
package multithreading.module;
import java.util.Stack;
/**
*用来存放调用者的请求, 其实就是一个producer/consumer队列
*/
public class ActiveQueue {
private Stack<MethodRequest> _queue;
private final static int QUEUE_SIZE = 20;
public ActiveQueue(){
_queue = new Stack<MethodRequest>();
}
public synchronized void enqueue(MethodRequest mr){
while(_queue.size() > QUEUE_SIZE){
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
_queue.push(mr);
notifyAll();
System.out.println("Leave Queue");
}
public synchronized MethodRequest dequeue(){
MethodRequest mr;
while(_queue.empty()){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mr = _queue.pop();
notifyAll();
return mr;
}
}
package multithreading.module;
/**
*封装并发逻辑的活动对象
*/
public class ActiveObject extends Thread {
private ActiveQueue _queue;
public ActiveObject(){
_queue = new ActiveQueue();
start();
}
public void enqueue(MethodRequest mr){
_queue.enqueue(mr);
}
public void run(){
while(true){
MethodRequest mr = _queue.dequeue();
mr.call();
}
}
}
通过上面的代码可以看出正是这些类相互合作完成了对并发逻辑的封装。开发者只需要根据需要实现MethodRequest接口,另外再定义一个服务代理类提供给使用者,在服务代理者类中把服务调用者的请求转化为MethodRequest实现,交给活动对象即可。
使用该框架,可以较好的做到应用逻辑和并发模型的分离,从而使开发者集中精力于应用领域,然后平滑的和并发模型结合起来,并且可以针对ActiveQueue定制排队机制,比如基于优先级等。
基于框架的解决方案
使用上述的框架重新实现前面的例子,提供对于并发的支持。第一步先完成对于MethodRequest的实现,对于我们的例子来说实现如下:
package multithreading.module;
public class SayHello implements MethodRequest {
private Service _service;
public SayHello(Service s){
_service = s;
}
@Override
public void call() {
_service.sayHello();
}
}
该类完成了对于服务提供接口sayHello方法的封装。接下来定义一个服务代理类,来完成请求的封装、排队功能,当然为了做到对Client透明,该类必须实现Service接口。定义如下:
package multithreading.module;
public class ServiceProxy implements Service {
private Service _service;
private ActiveObject _active_object;
public ServiceProxy(){
_service = new ServiceImp();
_active_object = new ActiveObject();
}
@Override
public void sayHello() {
MethodRequest mr = new SayHello(_service);
_active_object.enqueue(mr);
}
}
其他的类和接口定义不变,下面对比一下并发逻辑增加前后的服务调用的变化,并发逻辑增加前,对于sayHello服务的调用方法为注释部分;并发逻辑增加后,对于sayHello服务的调用方法为未注释部分。
package multithreading.module;
public class TestMain {
public static void main(String[] args) {
//单线程版本
/*Service service = new ServiceImp();
Client client = new Client(service);
client.requestService();*/
//多线程版本
Service service = new ServiceProxy();
Client client = new Client(service);
client.requestService();
}
}
可以看出并发逻辑增加前后对于Client的ServiceImp都无需作任何改变,使用方式也非常一致,ServiceImp也能够独立的进行重用。类结构图如下: