java单例模式(Singleton)

来源:互联网 发布:苹果手机绘图软件 编辑:程序博客网 时间:2024/04/28 20:21

普通的单例模式

public final class EagerSingleton   {       private static EagerSingleton singObj = new EagerSingleton();          private EagerSingleton(){       }          public static EagerSingleton getSingleInstance(){          return singObj;     }   }  

这就是饥饿模式对象在没有使用之前就已经初始化了。但这样会产生潜在的性能问题如果这个对象很大呢?没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。我们需要一种新的设计思想:延迟加载

public final class LazySingleton   {       private static LazySingleton singObj = null;          private LazySingleton(){       }          public static LazySingleton getSingleInstance(){           if(null == singObj ) singObj = new LazySingleton();           return singObj;     }   } 

这种写法就是所谓的懒汉模式它使用了延迟加载来保证对象在没有使用之前,是不会进行初始化的。但这样仍然有问题,线程不安全啊,这样。怎么办?很简单,在那个方法前面加一个Synchronized就OK了。

public final class ThreadSafeSingleton   {       private static ThreadSafeSingleton singObj = null;          private ThreadSafeSingleton(){       }          public static Synchronized ThreadSafeSingleton getSingleInstance(){           if(null == singObj ) singObj = new ThreadSafeSingleton();             return singObj;     }   }

同步的代价必然会一定程度的使程序的并发度降低那么有没有什么方法,一方面是线程安全的,有可以有很高的并发度呢?线程不安全的原因其实是在初始化对象的时候,所以,可以想办法把同步的粒度降低,只在初始化对象的时候进行同步。这里有必要提出一种新的设计思想双重检查锁(Double-Checked Lock)。

public final class DoubleCheckedSingleton   {       private static DoubleCheckedSingletonsingObj = null;          private DoubleCheckedSingleton(){       }          public static DoubleCheckedSingleton getSingleInstance(){           if(null == singObj ) {               Synchronized(DoubleCheckedSingleton.class){                      if(null == singObj)                            singObj = new DoubleCheckedSingleton();               }          }        return singObj;     }   } 

这种写法使得只有在加载新的对象进行同步,在加载完了之后,其他线程在第九行就可以判断跳过锁的的代价直接到第15行代码了。做到很好的并发度。上面的写法一方面实现了延迟加载,另一个方面也做到了并发度很好的线程安全,一切看上很完美。其实这种写法还是有问题的!!假设线程A执行到了第9行,它判断对象为空,于是线程A执行到第12行去初始化这个对象,但初始化是需要耗费时间的,但是这个对象的地址其实已经存在了。此时线程B也执行到了第九行,它判断不为空,于是直接跳到15行得到了这个对象。但是,这个对象还没有被完整的初始化!得到一个没有初始化完全的对象有什么用!!关于这个Double-Checked Lock的讨论有很多,目前公认这是一个Anti-Pattern,不推荐使用!

那么有没有什么更好的写法呢?有!这里又要提出一种新的模式——Initialization on Demand Holder. 这种方法使用内部类来做到延迟加载对象,在初始化这个内部类的时候,JLS(Java Language Sepcification)会保证这个类的线程安全。这种写法最大的美在于,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。

public class Singleton     {         private static class SingletonHolder         {             public final static Singleton instance = new Singleton();         }             public static Singleton getInstance()         {             return SingletonHolder.instance;         }     }   
Double-Checked Lock:http://en.wikipedia.org/wiki/Double-checked_locking
Initialzation on Demand Holder:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom