[关闭]
@BertLee 2017-08-03T06:06:00.000000Z 字数 2636 阅读 991

遍体鳞伤的单例模式

前言

  单例模式随处可见,用处也很大,但是写好一个单例模式很不容易,拔出萝卜带出泥,面试也经常会被问到,今天就来总结一下。

  什么是单例?

  1. 单例类只能有1个实例对象,
  2. 单例类必须自己创建该实例,别人不能创建
  3. 单例类必须该所有其他对象提供该实例

1. 饥饿式

  1. /**
  2. * 1-饿汉式单例类
  3. */
  4. public class EagerSingleton {
  5. //private保证实例不被直接访问,static保证只有1份,final保证这1份不被修改
  6. private static final EagerSingleton instance = new EagerSingleton();
  7. //private保证不被其它类new出来
  8. private EagerSingleton(){}
  9. //静态工厂方法,给其它对象提供相同方法
  10. public static EagerSingleton getInstance(){
  11. return instance;
  12. }
  13. }

  优点:在类被加载的时候,就创建了实例,多线程下是安全的。
  缺点:创建单例的时机过于急躁,在没有用到单例之前,占用了内存,而且初始化加载的配置到最后可能并不是我们想要的

2. 懒汉式

  1. /**
  2. * 2-懒汉式单例模式
  3. */
  4. public class LazySingleton {
  5. //当getInstance方法被调用时才会被创建,没有final修饰,不能保证线程安全
  6. private static LazySingleton instance = null;
  7. private LazySingleton(){}
  8. public synchronized static LazySingleton getInstance() {
  9. if(instance == null){
  10. //在多线程情况下,实例的创建会出现现不一致情况,因此要加synchronized
  11. instance = new LazySingleton();
  12. }
  13. return instance;
  14. }
  15. }

  优点:保证了线程安全,只有在被用到的情况下,才会初始化实例,节省了空间
  缺点:synchronized加在了方法上,同步过度,每个线程执行getInstance方法时都要同步,执行效率比较低,最好的情况是只在实例第一次初始化时,执行同步

3. 双重检查锁

  1. /**
  2. * 3-双重检查锁单例模式
  3. */
  4. public class Singleton implements Serializable {
  5. private static volatile Singleton instance = null;
  6. private Singleton() {
  7. }
  8. public static Singleton getInstance() {
  9. //如果实例已经被创建,则不被同步,这样可以提高执行效率
  10. if (instance == null) {
  11. //实例初次被创建时,可能有多个线程,所以要加锁,同步
  12. synchronized (Singleton.class) {
  13. //实例只可以被创建1次,等其它阻塞线程执行时,不在初始化
  14. if (instance == null) {
  15. //该赋值操作,只在当前线程的工作区中执行,什么时候会写到主存,由jvm执行,因此要加volatile,让其它线程可知
  16. instance = new Singleton();
  17. }
  18. }
  19. }
  20. return instance;
  21. }
  22. //防被反序列化时创建多个实例对象
  23. private Object readResolve() {
  24. return getInstance();
  25. }
  26. //防止通过反射创建多个实例,重写getClass方法
  27. private static Class getClass(String classname)
  28. throws ClassNotFoundException {
  29. ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  30. if (classLoader == null)
  31. classLoader = Singleton.class.getClassLoader();
  32. return (classLoader.loadClass(classname));
  33. }
  34. }

  优点:理想很丰满,思路清晰,只在初始化实例时,同步
  缺点:现实很残酷,在java旧版本时,支持的并不很好,原因在于instance = new Singleton()在多线程情况下,可能出现构造方法还没执行,就被其它线程拿走了。现如今violate已被优化,可以实现,但是效率并不高

4. 静态内部类

  1. /**
  2. * 4-内部类单例模式
  3. */
  4. public class InnerClassSingleton {
  5. private static class SingletonHolder{
  6. public static final InnerClassSingleton instance = new InnerClassSingleton();
  7. }
  8. public static InnerClassSingleton getInstance(){
  9. return SingletonHolder.instance;
  10. }
  11. }

  优点:由jvm保证线程安全,只有被用到时,才会被初始化
  缺点:序列化和反序列化支持比较差

5. 枚举类

  1. /**
  2. * 5-枚举单例类
  3. */
  4. public enum SingletonEnum {
  5. INSTANCE; //单例实例
  6. private DataSource dataSource;
  7. private SingletonEnum(){
  8. //初始化单例的内容,添加dataSource的配置内容
  9. }
  10. public Connection getConnection() throws SQLException {
  11. return dataSource.getConnection();
  12. }
  13. }

  优点:最佳实践,完美解决各种问题
  缺点:项目中运用的少

后记

  老朽在撰笔之时,参考了以下几篇博文,如有不妥之处,请与老朽联系。

http://blog.csdn.net/huangyuan_xuan/article/details/52193006
http://blog.csdn.net/yangkai_hudong/article/details/50628172
http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html

  转载注明出处是人格的一种体现。
  能力有限,如有纰漏,请在评论区指出,老朽虽一把年纪,必当感激涕零,泪如雨下。若有满嘴喷粪撕逼者,一律拉黑、举报,并移交阎王爷处理。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注