单例模式static的困惑

07-05-23 leoyu
              

单例:客户不能自己通过构造器实例化对象,只能通过类中静态方法返回此对象的静态实例。

但这样我感觉很多都应该设计成单例,因为一般的都是以方法传参数然后执行出结果。这样的话不涉及到对象状态的问题。

状态问题只涉及到实例对象或类对象的情况。对于局部变量都会有一个复本。这是不存在对象状态的问题的(我的理解)。

但JAVA编程思想上说static用多了就应该考虑你的设计是否有问题了。感觉有些矛盾。大家谈谈是否应该在service层,dao层多多使用单例呢?或者是不是应该多使用静态方法呢???

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

              

leoyu
2007-05-23 18:06

例如:

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修改过]

leoyu
2007-05-23 18:27

我有新的看法,嗯.我以上说的,我认为是没错.也不会存在安全性的问题.也有一定的性能提升.

但我认为单例是设计上的问题,即,如果把很多无状态对象(可能很大)都做成单例,那这样单例在JVM中就一直存在着.这就相当于一个篮子里放着很多鸡蛋(单例对象).如果一个内存为1024M.那可能对象多了,单例在内存中就占了20M(假如,也许更多),那这样是得不到释放的,而如果不是单例对象,则GC可能在一个时间段会回收掉资源.我认为这是单例使用的关键吧.即在使用单例模式与否的关键要做一定判断,比如对象不是很大(当然是没状态的),这样的可以设计成单例,而不是所有的都设计成单例的.

嗯,以上是我的认识,不知道banq以及其它道友的认识是怎样的?

以上理由就是我用来说明大家说不要过于使用单例的原因。

关于性能我还要说一点自己的看法,如下:

应该在能控制,清楚地使用实例变量,而不是全都(盲目)使用局部变量(因为每一个局部变量都有不同的复本,这样开销性能是很大的,当然局部变量生命周短,但并发人数多的数,在这个时间段时内在开销是很大的,但如果加入缓存技术,应该就没有这个问题了).所以我推荐在'只读'对象上实例变量,这样会带来性能上一定的提高.对于可写的对象,那还要在局部变量和实例变量同步性能上的性能考虑/

嗯.以上是我对这些细节的一些看法,欢迎拍砖~~

但对于大的对象,我还是认为在之初就初始化好这样是个不错的方案

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

leoyu
2007-05-24 01:28

DAO层通过是简单的无状态的CRUD操作,可以完全放心的使用单例来提升性能.而SERVICE就不一定了.可能会有状态,也有可能要扩展.所以最好不要设计成单例.这是一个设计上的问题

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

banq
2007-05-24 15:54

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

关键点是:单例模式和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修改过]

2Go 1 2 下一页