系统并发杂谈

 

转自:http://www.ibm.com/developerworks/cn/java/l-multithreading/  并增加自己的分析,希望对并发系统的设计提供一种思路。

首先举一个简单的例子系统中有一个服务提供者,他通过接口对外提供服务,比如打印hello world。

//定义接口
public interface Service {
    public void sayHello();
}
//接口实现
public class ServiceImp implements Service {
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

//接口调用者
public class Client {
    public Client(Service s) {
        _service = s;
    }

    public void requestService() {
        _service.sayHello();
    }

    private Service _service;
}
//主程序
public class Main {

    public static void main(String[] args) {
        /*
        并发逻辑增加前,对于sayHello服务的调用方法
         */
        Service s = new ServiceImp();
        Client c = new Client(s);
        c.requestService();
        
    }
}

如果现在有新的需求,要求该服务必须支持Client的并发访问。一种简单的方法就是在原来的接口上加上synchronized 声明,但是带来的性能损失相当大,这种方案基本被毙掉(当然对于本例来说,目前是没有必要的,因为ServiceImp没有需要保护的数据,但是随着需求的变化,以后可能会有的)。而且这样的话就要在原来的代码上改,成本较大。而且并发逻辑和应用逻辑耦合在一起,对代码的可读性和可维护性都是极大的损害。如果能做到将并发逻辑和应用逻辑分离开来,那么对系统的侵入性将大大降低,应用逻辑的自身可以被很好的复用。造成Client阻塞,性能降低以及无法满足并发的很大原因是所有的服务调用都是同步的。现在看下改为异步的情况。

     核心就是使用主动对象来封装并发逻辑,然后把Client的请求转发给实际的服务提供者(应用逻辑),这样无论是Client还是实际的服务提供者都不用关心并发的存在,不用考虑并发所带来的数据一致性问题。从而实现应用逻辑和并发逻辑的隔离,服务调用和服务执行的隔离。下面给出关键的实现细节。

本框架有如下几部分构成:

  1. 一个ActiveObject类,从Thread继承,封装了并发逻辑的活动对象
  2. 一个ActiveQueue类,主要用来存放调用者请求
  3. 一个MethodRequest接口,主要用来封装调用者的请求,Command设计模式的一种实现方式

/**
 * 封装调用者的请求,就是封装了一个对外的接口
 */
public interface MethodRequest {
    public void call();
}

/**
*用来存放调用者请求,就是一个并发栈的实现
*/
public class ActiveQueue {
    public ActiveQueue() {
        _queue = new Stack();
    }

    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 {
                System.out.println("开始尝试获取队列");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        mr = (MethodRequest) _queue.pop();
        notifyAll();

        return mr;
    }

    private Stack _queue;
    private final static int QUEUE_SIZE = 20;

}


/**
 * 线程对象,封装了异步并发逻辑
 */
public class ActiveObject extends Thread {
    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();
        }
    }

    private ActiveQueue _queue;
}


其实就是用一个新的线程去队列里取请求来执行,只是请求都统一封装成MethodRequest 接口的形式,另外ActiveQueue 的实现有点低效,可以用java并发包里的实现。对于这个例子,首先要把实际的逻辑封装成MethodRequest 

public class SayHello implements MethodRequest {
    public SayHello(Service s) {
        _service = s;
    }

    public void call() {
        _service.sayHello();
    }

    private Service _service;
}

接下来当然是需要将请求的封装、排队,执行包装好对外就是统一入口,为了做到对Client透明,该类必须实现Service接口。定义如下:

public class ServiceProxy implements Service {
    public ServiceProxy() {
        _service = new ServiceImp();
        //这里就启动了线程监听队列
        _active_object = new ActiveObject();
    }

    public void sayHello() {
        MethodRequest mr = new SayHello(_service);
        _active_object.enqueue(mr);
    }

    private Service _service;
    private ActiveObject _active_object;
}

其他的逻辑代码都没有变,可以看下修改前后的调用

public class Main {

    public static void main(String[] args) {
        /*
        并发逻辑增加前,对于sayHello服务的调用方法
         */
//        Service s = new ServiceImp();
//        Client c = new Client(s);
//        c.requestService();

        /*
        并发逻辑增加后,对于sayHello服务的调用方法
         */
        Service s = new ServiceProxy();
        Client c = new Client(s);
        c.requestService();
    }
}

可以看到应用逻辑的实现Service的实现ServiceImp并没有改变,他只是单纯的实现了应用的逻辑,没有关心是否需要并发。当然这个例子只是举了一个很简单的场景,只需要调用不需要返回值,但是在很多场景中调用是需要返回值的,这个值得再思考下。




 

你可能感兴趣的:(系统并发杂谈)