active object 是一种异步编程的设计模式,通过对异步对方法的调用(Method invocation)和方法的执行(Method Execution)进行解耦(Decoupling),从而提高程序运行效率。
Active Object模式的核心是他允许任务的提交(相当于对异步方法的调用)和任务的执行(相当于异步方法的真正执行)分离。这点类似与System.gc()方法。
System.gc():
客户端调用完垃圾回收的gc后,一个进行垃圾回收的任务被提交,但此时不一定进行垃圾回收,而是调用了gc(),一个进行垃圾回收的任务被提交,但此时JVM不一定立即开始垃圾回收,而可能是在gc()方法被调用后的一段时间之后才会执行垃圾回收。我们知道,System.gc()的调用方代码是运行在自己的线程上(通常是main线程派生的子线程),而JVM的垃圾回收这个动作则由专门的工作者线程(垃圾回收线程)来执行。换而言之,System.gc()这个方法所代表的动作(其所定义的功能)的调用方法和执行方法是运行在不同的线程中的,从而提高了并发性。
核心类:Active Object 对外暴露,asyncService方法的调用方和执行方运行在个子的线程上,在多线程环境下,asyncService方法会被多个线程调用。
当Active Object对外暴露的异步方法被调用时,与该方法调用的上下文信息,包括:
【被调用的异步方法名 或其代表的操作】
【客户端端代码所传递的参数】
会被封装成一个对象。该对象被称为方法请求(Method Request)。
方法请求对象会被存入Active Object模式所维护的缓冲区(Activation Queue)中,并由专门的工作者线程负责根据其包含的上下文信息执行相应的操作。也就是说,方法请求所代表的操作则由专门的工作者线程来执行,从而实现了方法的调用和执行的分离,产生了并发。
Active Object模式的主要参与者有以下几种。其类图如图8-2所示。
- Proxy:负责对外暴露异步方法接口。其主要方法及职责如下。
- asyncServic:该异步方法负责创建于该方法相应的MethodRequest参与者实例,并将其提交给Scheduler参与者实例。该方法的返回值是一个Future参与者实例,客户端代码可以通过它获取异步方法对应的任务的执行结果。
- MethodRequest:负责将客户端代码对Proxy实例的异步方法的调用封装为一个对象。该对象保留了异步方法的名称及客户端代码传递的参数等上下文信息。它使得Proxy的异步方法的调用和执行分离成为可能。其主要方法及职责如下。
1.call:根据其所属MethodRequest实例所包含的上下文信息调用Servant实例的相应方法。 - ActivationQueu:缓冲区,用于临时存储由Proxy的异步方法被调用时所创建的MethodRequest实例。其主要方法及职责如下。
enqueue:将MethodRequest实例放入缓冲区
dequeue:从缓冲区中取出一个MethodRequest实例 - Schedule:负责将Proxy的异步方法所创建的MethodRequest实例存入其维护的缓冲区中,并根据一定的调度策略,对其维护的缓冲区中的MethodRequest实例进行执行。其调度策略可以根据实际需要来定,如FIFO、LIFO和根据MethodRequest中包含的信息所定的优先级等。其主要方法及职责如下。
enqueu:接受一个MethodRequest实例,并将其存入缓冲区。
dispatch:反复地从缓冲区中取出MethodRequest实例进行执行。 - Servant:负责Proxy所暴露的异步方法的具体实现。其主要方法及职责如下。
doSomething:执行Proxy所暴露的异步方法对应的任务。 - Future:负责存储和获取Active Object异步方法的执行结果。其主要方法及职责如下:
get:获取异步方法对应的任务的执行结果。
set:设置异步方法对应的任务的执行结果。
Active Object模式的序列图如图8-3所示。
第1步:客户端代码调用Proxy的异步方法asyncService。
第2-7步:asyncService方法创建Future实例作为该方法的返回值,并将客户端代码对该方法的调用封装为MethodRequest对象。然后以所创建的MethodRequest对象作为参数调用Scheduler的enqueue方法,以将MethodRequest对象存入缓冲区。Scheduler的enqueue方法会调用Scheduler所维护的ActivationQueue实例MethodRequest对象存入缓冲区。
第8步:asyncService返回其所创建的Future实例。
第9步:Schedule实例采用专门的工作者线程运行dispatch方法。
第10-12步:dispatch方法调用ActivationQueue实例的dequeue方法,获取一个MethodRequest对象。然后调用MethodRequest对象的call方法。
第13-16步:MethodRequest对象的call方法调用与其相关联的Servant实例的相应方法doSomethin,并将Servant.doSomething方法的返回值设置到Future实例上。
第17步:MethodRequest对象的call方法返回。
上述步骤中,第1-8步是运行在Active Object的客户端线程中的,这几个步骤实现了将客户端代码对Active Object所提供的异步方法的调用封装成对象(MethodRequest),并将其存入缓冲区(ActivationQueue)。这几个步骤实现了任务的提交。第9-17步是运行在ActiveObject的工作者线程中,这些步骤实现从缓冲区中读取MethonRequest,并对其进行执行,实现了任务的执行。从而实现了Active Objec对外暴露的异步方法的调用与执行的分离。
如果客户端代码关系Active Object的异步方法的返回值,则可以在其需要时,调用Future实例的get方法来获取异步方法的真正执行结果。