在看spring reference的时候看到spring 中关于异步task的封装,感觉很有意思,试了一下。
简单的说,spring 对java的TaskExecutor进行了封装,可以像pojo一样使用,事实上,我们也可以直接使用TaskExecutorService(如果我没有记错,java里是这个类吧),当然也可以自己实现,但spring提供了一些类,比如ThreadPoolTaskExecutor,为什么不使用呢?当然更重要的是它在配置文件中提供了一种简便方式<task-excutor/>,就相当于声明了一个ThreadPoolTaskExecutor的bean。接下来一步步开始实验。
1, 首先在context.xml中做一些声明,配置ThreadPoolTaskExecutor,以及使用anotation driven
<context:component-scan base-package="test" />
<task:executor id="myExecutor" pool-size="2" />
<task:annotation-driven executor="myExecutor" />
2, 编写需要异步执行的代码。
@Async
public Future do() {
........
return new AsyncResult(...);
}
这个地方是使我很吃惊的地方。这里只需要做两件事,annotate这个方法为@Async,以及让这个方法返回Future对象(如果这个方法不需要返回值,那这个也不需要,只需要返回void)。而且事实上如果需要传入参数,也可以像其他的方法一样传入。按照文档上说,这样做了以后,当我们调用这个方法以后,会直接返回,但是需要body部分会异步执行,我们所要做的,只需要等在Future.get()上就可以了。神奇吧?
3, 接下来就像其他的bean一样了,caller和callee,通过context得到,就像调用普通方法一样调用就可以了。
4, 一些问题
当然也遇到了一些问题,不然就像上面简单罗列一下,还不如直接把文档里copy过来。
a, 一开始,我直接把这个异步方法放在main() (main用来启动context).所在的类里, 结果发现,这个方法事实上没有在异步执行。我搜了一些资料,发现做法和我差不了多少。这种简单的应用spring应该不会有问题吧?我想到spring肯定是要为这个方法生成proxy,它只是替我们做了很多生成thread,future的事情,也许问题出在spring不能为main所在的类生成proxy上面,或者至少是不能为这样的异步方法生成proxy。我把这个方法移到一个新的类中去以后,果然就成功了。当然可能还有更detail的原因。
b, 按a做了以后很happy。但等程序执行以后,这种happy慢慢地没有了,因为我发现程序过了很久都没有结束。又过了很久还是没有结束(我是用eclipse执行的,从console状态可以看出程序仍然在运行)。我猜这种情况应该是因为那两个异步的任务没有结束掉造成的。我用jconsole查看了一下,果然发现那个异步线程wait中,main线程已经结束了。郁闷。如果是我们自己写的thread,任务完成后自然会停掉,但spring替我们生成的线程还要我们去管理,这个太不爽了。但好在还是有办法处理。就是通过context得到threadPoolTaskExecutor,然后调用destroy方法。