单例Singleton和Prototype模式(Pool多例池化)曾经是两个截然相反的模式,EJB的无态会话Bean最早是以Pool形式出现,后来颠覆EJB的Spring提出Singleton,所有业务类缺省都是单例的,当时曾经引起本人的大跌眼镜,也发过抱怨,单例中状态访问需要注意锁模式。
TSS网站曾经有人提出Object Pool是性能杀手,引起轩然大波,参与讨论人数之最大概算TSS有史以来最长帖子,在Java并发编程一书中,作者认为大的类可以使用Pool,小类使用Pool反而会降低性能,本人亲自动手,对Jdon框架6.0中一个案例小应用分别在单例和Pool下进行测试,这个类代码不大,属于小类,发现两者性能区别不大,都是大约70毫秒左右。
后来有人写了Object Pooling - Determinism vs. Throughput(宿命论 vs. 吞吐量),提出Performance does not always mean Throughput,性能并不总是意味吞吐量,Pool通过对象池最大个数来限制Throughput吞吐量,但是限制吞吐量是为了保证现有通过的请求响应能够快速完成,如果没有最大个数限制,不断增加的请求会拖慢每个请求的响应时间。作者提出几种情况下使用Pool:
1.GC微调不能帮助你
2.应用创建大量对象,你的对象创建是很费时,但是易于循环使用。
3.宿命论,响应时间相对对于吞吐量非常重要。
http://michael-bien.com/mbien/entry/object_pooling_determinism_vs_throughput
前面谈了POOL模式使用场景,其实也是EJB无态Bean的使用场景,因为它缺省是POOL的,无论EJB1/EJB2/EJB3以及即将到来的JavaEE 6.0中的EJB3.1,都是这样。
不过,EJB拒绝单例的先例从JavaEE 6开始打破,引入了单例注解,这样,对于应用就有些选择,但是这个单例还是逃不了分布式环境中没有绝对单例的前提,只有每一个JVM是单例,但是EJB3.1没有提及如何保证在整个集群环境下支持单例,但是规定谈是一个应用Applcation scope下的单例,那么集群部署多台机器也应该全部属于一个Applcation。Although the specification does not cover clustering support, it is very likely that most vendors will make Singletons cluster-safe。(
http://www.theserverside.com/tt/articles/article.tss?l=NewFeaturesinEJB3-1)
正如我以前担心一样,引入单例就容易导致并发问题,需要小心处理,JavaEE 6为Singleton的引入提供了辅助的大量注解:
1.如@Startup,你可以在应用启动时,象auotexec,bat那样单例启动一些工作。
2.并发,有container-managed concurrency 和 bean-managed concurrency.这就复杂了,从这个单例带来的复杂性也可以想象,当初Spring冒然引入单例带来棘手问题,这也是我当时担心反感的理由,没有做到象今天EJB3.1中不但做到拉了屎,还要负责擦屁股。
引入单例的另外一个最大好处就是:面向内存编程,我一直提倡的面向缓存状态编程的模式就可以在JavaEE标准中实现,正如我在JiveJdon中实现并倡导的那样,使用并发锁模式性能要优于铁板一块的事务机制。
具体EJB 3.1 Singleton处理见这里:
http://java.sun.com/javaee/6/docs/tutorial/doc/gipvi.html
[该贴被banq于2009-08-11 20:15修改过]