记得好像有本书叫做《java程序员必会的23中设计模式》,作为一个java程序员,我其实没有读过,呵呵。在这里就是借用一下23这个数字。但具体是不是只有23中呢?我想未必。是不是只有java程序员才需要学呢?也未必。关于设计模式的资料看过一些,但基本上是当时看明白了,过后就忘了,这是为什么呢?因为很少用到呗。没有用到印象就不会深刻。不过单例模式倒是常有用到,而且有遇到过线程并发问题,印象比较深刻。所以就总结一下。有什么不对的地方大家指出啊 :)
单例模式顾名思义,就是只有一个实例。当一个类的实例可以有且只可以一个的时候就需要用到了。为什么只需要有一个呢?有人说是为了节约内存。本人对这个说法持保留态度。只有一个实例确实减少内存占用,可是我认为这不是使用单例模式的理由。我认为使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候。比如类似有序的号码生成器这样的东西,怎么可以允许一个应用上存在多个呢?
单例模式到底怎么写呢?我想大多数的程序员都知道下面这种方式(我当时就这么写的,从网上学的哦):
私有构造函数保证该类不能被别人直接New出来,只能通过静态的getInstance()方法得到实例。而且实例作为内部的一个静态对象存在,应该是在整个程序中唯一了。貌似一个单例模式就这么简单的别搞定了。我曾经就这么天真的认为,是这样。可是,剧情的设计通常是天真的小孩总会遇到妖精。
单线程的时候一切如设想的一样,没有任何问题。可是不管是web程序,还是EJB应用,都是多线程的,每个会话都有自己的线程。当两个线程几乎同时去getInstance()的时候,或许只差那么0.0000000001ms,程序走到if (instance==null) 这一行时,妖精出现了,两个线程同时断定(instance==null)为true,然后理所当然的都进行了 new Singleton()的操作并返回了实例。两个获取实例的线程所持有的Singleton实例将是不同的实例。此时,单例被破坏了,我们只能祈祷不要引起数据或者逻辑的混乱了。这种情况一旦发生,是无法跟踪调试,也是很难重现错误的。通常这种时候,用户就会打来电话,像唐僧看到妖精似的对你说:“有妖精,有妖精!”作为悟空你就会拿起金箍棒到处找妖精准备降妖,可是怎么也找不到。然后摸着脑袋说:“没有妖精啊?”实际上妖精是有的,而且总有一天会掳走唐僧。
那怎么办呢?任何妖精都有降伏的方法,而且方法可能很简单:
直接new 一个实例赋给静态变量,任何时候在一个应用中这行代码只会执行一次,而getInstance() 只是返回了一下已经初始化好的对象而已。多线程同时调用getInstance()的时候也不回出现返回的对象不是同一个的情况了。当然,这只是解决单例模式多线程问题的一个简单的方法。方法是有很多的,这里就不多说了。
其实单例模式是很简单的,不过用的时候一定要考虑的多线程的时候并发的问题。处理好多线程并发才是单例模式的关键。
单例模式顾名思义,就是只有一个实例。当一个类的实例可以有且只可以一个的时候就需要用到了。为什么只需要有一个呢?有人说是为了节约内存。本人对这个说法持保留态度。只有一个实例确实减少内存占用,可是我认为这不是使用单例模式的理由。我认为使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候。比如类似有序的号码生成器这样的东西,怎么可以允许一个应用上存在多个呢?
单例模式到底怎么写呢?我想大多数的程序员都知道下面这种方式(我当时就这么写的,从网上学的哦):
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
f (instance==null)
instance=new Singleton();
return instance;
}
}
私有构造函数保证该类不能被别人直接New出来,只能通过静态的getInstance()方法得到实例。而且实例作为内部的一个静态对象存在,应该是在整个程序中唯一了。貌似一个单例模式就这么简单的别搞定了。我曾经就这么天真的认为,是这样。可是,剧情的设计通常是天真的小孩总会遇到妖精。
单线程的时候一切如设想的一样,没有任何问题。可是不管是web程序,还是EJB应用,都是多线程的,每个会话都有自己的线程。当两个线程几乎同时去getInstance()的时候,或许只差那么0.0000000001ms,程序走到if (instance==null) 这一行时,妖精出现了,两个线程同时断定(instance==null)为true,然后理所当然的都进行了 new Singleton()的操作并返回了实例。两个获取实例的线程所持有的Singleton实例将是不同的实例。此时,单例被破坏了,我们只能祈祷不要引起数据或者逻辑的混乱了。这种情况一旦发生,是无法跟踪调试,也是很难重现错误的。通常这种时候,用户就会打来电话,像唐僧看到妖精似的对你说:“有妖精,有妖精!”作为悟空你就会拿起金箍棒到处找妖精准备降妖,可是怎么也找不到。然后摸着脑袋说:“没有妖精啊?”实际上妖精是有的,而且总有一天会掳走唐僧。
那怎么办呢?任何妖精都有降伏的方法,而且方法可能很简单:
public final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static synchronized Singleton getInstance() {
return instance;
}
}
直接new 一个实例赋给静态变量,任何时候在一个应用中这行代码只会执行一次,而getInstance() 只是返回了一下已经初始化好的对象而已。多线程同时调用getInstance()的时候也不回出现返回的对象不是同一个的情况了。当然,这只是解决单例模式多线程问题的一个简单的方法。方法是有很多的,这里就不多说了。
其实单例模式是很简单的,不过用的时候一定要考虑的多线程的时候并发的问题。处理好多线程并发才是单例模式的关键。