设计模式之单例模式(上)(创建者模式)

69 阅读3分钟

什么是单例模式?

单例模式属于创建型模式.他提供了一种创建对象的最佳方式,这种模式只涉及到单一的类,该类的唯一职责就是创建自己的对象,同时确保这个对象只能被创建的一次.并且这个类提供一个访问该对象的唯一方式,直接访问,使用是不再需要实例化对象

注意:

  • 单例类只能有一个实例.
  • 单例类的实例必须是自己的创建且唯一的实例
  • 单例类必须给其他所有对象提供这个一个实例

单例模式的两种实现

饿汉式

  • 基本实现
     public class Singleton {
         private Singleton() {}

         private static final Singleton INSTANCE = new Singleton();

         public static Singleton getInstance() {
             return INSTANCE;
         }
     }

总结:这种方式简单易用,执行效率高,但是他在类加载的时候就初始化,如果该对象只是声明,在后续代码中并没有使用,那么他申请的内存无疑是浪费的

  • 枚举实现
     public enum Singleton {
        INSTANE;
     }

总结:这种方式利用了 enum 只会被加载一次的机制,实现了实例化对象.这也是最佳的实现单例的模式的方式,在不考虑内存是否浪费的情况下

懒汉式

  • 基本实现(线程不安全)
       public class Singleton {
           private Singleton() {}
   
           private static Singleton INSTANCE;
   
           public static Singleton getInstance() {
               if (INSTANCE == null){
                   new Singleton();
               }
               return INSTANCE;
           }
       }

总结:这种方式简单易用,执行效率高,且只有在该对象被调用时才会实例化,不会出现内存浪费.但是在多线程的情况下会出现问题,他是线程不安全的

  • 基本实现(线程安全)
      public class Singleton {
          private Singleton() {}
  
          private static Singleton INSTANCE;
  
          public static synchronized Singleton getInstance() {
              if (INSTANCE == null){
                  new Singleton();
              }
              return INSTANCE;
          }
      }

总结:这种方式在多线程的情况下也是安全的,但是因为加了 synchronized 关键字影响了执行效率

  • 双重锁实现
        public class Singleton {
            private Singleton() {}
            private static Singleton INSTANCE;
            public static Singleton getInstance() {
                if (INSTANCE == null){
                    synchronized (Singleton.class) {
                        if (INSTANCE == null){
                            INSTANCE = new Singleton();
                        }
                    }
                }
                return INSTANCE;
            }
        }

总结:这种方式使用双重锁机制,不是在每次请求的时候都要加锁,只有在进行创建对象时才会加锁,即保证了线程安全同时也保证了执行效率.但是这种也会有另一种情况发生 即 在多线程情况下,jvm 在进行实例化对象时会进行指令重排操作,进而可能出现空指针异常,这时需要加上 volatile 关键字进行修饰,保证变量的可见性和有序性

        public class Singleton {
            private Singleton() {}
            private static volatile Singleton INSTANCE;
            public static Singleton getInstance() {
                if (INSTANCE == null){
                    synchronized (Singleton.class) {
                        if (INSTANCE == null){
                            INSTANCE = new Singleton();
                        }
                    }
                }
                return INSTANCE;
            }
        }
  • 静态内部类实现
       public class Singleton {
          private Singleton(){}
           private static class SingletonHolder {
               private static final Singleton INSTANCE = new Singleton();
           }
           public static Singleton getInstance() {
               return SingletonHolder.INSTANCE;
           }
       }

总结:这种方式实现简单,并且一样可以保证线程安全且保证效率.因为他利用了 classloader 的加载原理 和 static 关键字的作用来保证静态内部类种的静态属性只会被创建一次,而且也只会在该对象被调用时才会创建实例对象