单例模式static的困惑

单例:客户不能自己通过构造器实例化对象,只能通过类中静态方法返回此对象的静态实例。
但这样我感觉很多都应该设计成单例,因为一般的都是以方法传参数然后执行出结果。这样的话不涉及到对象状态的问题。
状态问题只涉及到实例对象或类对象的情况。对于局部变量都会有一个复本。这是不存在对象状态的问题的(我的理解)。
但JAVA编程思想上说static用多了就应该考虑你的设计是否有问题了。感觉有些矛盾。大家谈谈是否应该在service层,dao层多多使用单例呢?或者是不是应该多使用静态方法呢???
[该贴被leoyu于2007年05月23日 12:15修改过]

例如:
public class UserDaoImpl implements UserDao{
private UserDaoImpl (){}
private static UserDao m = new UserDaoImpl ();
public static UserDao getMM(){
return m;
}
public User login(User user) {
//do something
return user;
}
}
以上代码是单例在DAO层的应用(我觉得我们很多对象都是无状态的。都可以用单例来提升性能开销)。。。
但从多方资料看到说单例用多了设计有问题。我不知道哪有问题。呵呵。。而spring中默认的也是单例的。 那这是否意为着,要多考虑使用单例呢?

[该贴被leoyu于2007年05月23日 20:09修改过]

我有新的看法,嗯.我以上说的,我认为是没错.也不会存在安全性的问题.也有一定的性能提升.
但我认为单例是设计上的问题,即,如果把很多无状态对象(可能很大)都做成单例,那这样单例在JVM中就一直存在着.这就相当于一个篮子里放着很多鸡蛋(单例对象).如果一个内存为1024M.那可能对象多了,单例在内存中就占了20M(假如,也许更多),那这样是得不到释放的,而如果不是单例对象,则GC可能在一个时间段会回收掉资源.我认为这是单例使用的关键吧.即在使用单例模式与否的关键要做一定判断,比如对象不是很大(当然是没状态的),这样的可以设计成单例,而不是所有的都设计成单例的.
嗯,以上是我的认识,不知道banq以及其它道友的认识是怎样的?
以上理由就是我用来说明大家说不要过于使用单例的原因。
关于性能我还要说一点自己的看法,如下:
应该在能控制,清楚地使用实例变量,而不是全都(盲目)使用局部变量(因为每一个局部变量都有不同的复本,这样开销性能是很大的,当然局部变量生命周短,但并发人数多的数,在这个时间段时内在开销是很大的,但如果加入缓存技术,应该就没有这个问题了).所以我推荐在'只读'对象上实例变量,这样会带来性能上一定的提高.对于可写的对象,那还要在局部变量和实例变量同步性能上的性能考虑/
嗯.以上是我对这些细节的一些看法,欢迎拍砖~~

但对于大的对象,我还是认为在之初就初始化好这样是个不错的方案
[该贴被leoyu于2007年05月23日 19:50修改过]

DAO层通过是简单的无状态的CRUD操作,可以完全放心的使用单例来提升性能.而SERVICE就不一定了.可能会有状态,也有可能要扩展.所以最好不要设计成单例.这是一个设计上的问题
[该贴被leoyu于2007年05月24日 01:29修改过]

你的认识基本正确,但是深度不够,所以造成有些疑惑。

关键点是:单例模式和static是两码事情,单例模式可以使用static实现,也可以不由其实现。

>那可能对象多了,单例在内存中就占了20M(假如,也许更多),那这样是得不到释放的,
这个现象是针对static而言的。我们可以让一个对象在内存是单例,但是当这样对象多了的时候,也可以释放,被GC清除。

这就涉及容器概念,也就是说,我们将这些对象单例用一个容器包容,不是直接将这些单例放到JVM,而是放到一个容器中,这个容器再放到JVM中;如果我们消灭这个容器对象,那么其中的单例对象就会被GC回收。

这个时候,单例概念已经不是原来JVM单例概念,这个单例是一定范围(时间或空间)内的单例,实际等同于对象的scope。

类似Spring/jdonFramework这样框架,就是引入了这样一个容器,如果我们将这个容器对象scope设置为Web的Application级别,也就是说,在某个Web项目中,这个容器是唯一的,那么容器中的单例对象也就是唯一。如果我们Web项目不再部署,退出,容器对象生命周期结束,其中所有单例对象生命周期也就结束。

注意:虽然我们变通了单例的生命周期,但是在单例有生命时段内,如果多个线程访问(写操作)单例中的状态,那么就有数据不一致问题,好事人如果在这个单例对状态访问操作加上同步锁,那么就可能将多线程的J2EE/JavaEE应用变成单线程。

所以,如果我们的服务或DAO都是无状态的,那么使用单例没有问题,但是谁能保证程序代码不修改,如果来个鲁莽的小伙子,将你的服务改成有状态,那么就出现很多奇怪问题了。

当然,如果你有一个称不上好习惯的习惯,不喜欢在类的字段中保存数据,什么都依赖数据库,那么使用单例也会一直没有问题,但是这种习惯是OO的吗?高负载下性能会好吗?这又涉及另外问题了。

所以,养成用多例Pool来支持你的服务,不失为一个克服上面的两全其美的好办法,但是POOL也有小的性能开销。

其实绕来绕去,我们发现:当初我们使用EJB时的最大困惑:有态和无态的选择,在Spring/jdonFramework中不是被消灭了,而是被转移了,表面简化了,但是这个问题永远回避不了。

[该贴被banq于2007年05月24日 15:56修改过]

首先感谢banq的认真解答!
以下是我对banq兄回复的总结:
1.单例包装在一个'容器'里,由'容器'来管理单例的生命周期
2.应尽量使用pool来提升性能,避免过多的使用单例.

这里还请教一个问题,在JDON泡了这么久了,见BANQ大力推荐以设计模式+领域模型为出发点的DDD开发.在平常的时间里看设计模式还能看懂,但在实际应用中,确有想不出来在哪儿用设计模式,怎么用设计模式的问题.领域驱动设计这本书我看了下,看不大明白,不知是先看完设计模式再看领域模型呢还是先看领域模型再看设计模式.
以前没用开源框架前是古老的JSP+Servlet+JavaBean的MVC开发.现在用struts spring hibernate也有一段时间了,感觉都是一些框架应用上的问题.还是对软件构架,分析这块有更多的兴趣.希望BANQ能为我们只明一条比较适用的学习路线啊.感谢!


[该贴被leoyu于2007年05月29日 23:05修改过]

>希望BANQ能为我们只明一条比较适用的学习路线啊
多谢,这涉及其他话题,另开讨论吧。

关于POOL相关话题:

Flyweight模式之我见
http://www.jdon.com/jivejdon/thread/31903.html
[该贴被banq于2007年06月05日 12:12修改过]