天天看点

Java 设计模式——单例模式详解

  • 前言

    一直在学习Android, 但是都总觉得是在造轮子, 所以还是得把java学深刻, 不然很多线程不安全的东西就会出现.
  • 单例模式

    1. 饿汉式单例式

      所谓饿汉就是在程序启动或单例模式类被加载的时候, 单例模式实例就已经被创建
      public class Singleton{
          private static Singleton singleton = new Singleton();
          private Singleton(){}
          public static Singleton getInstance(){
              return singleton;
          }
      }
                 
      缺点: 不管是否需要都会创建这个对象实例, 但是我们总希望能够按需求做事.
    2. 懒汉式(线程不安全)

      懒汉: 当程序第一次访问单例模式实例时才进行创建.
      public class Singleton{
          private static Singleton singleton;
          private Singleton(){}
          public static Singleton getInstance(){
              if(singleton==null){
                  singleton = new Singleton();
              }
              return singleton;
          }
      }
                 
      缺点: 在多线程中不能正常工作,详情请点击: 解决单例设计模式中懒汉式线程安全问题
    3. 懒汉(线程安全)

      很明显下面代码中加了一个关键字 synchronized 锁
      public class Singleton{
          private static Singleton singleton;
          private Singleton(){}
          public static synchronized Singleton getInstance(){
              if(singleton==null){
                  singleton = new Singleton();
              }
              return singleton;
          }
      }
                 
      缺点: 虽然能在多线程正常工作, 效率太低.
    4. 静态内部类

      当Singleton类被加载的时候, singleton并没有被初始化, 因为SingletonHolder类没有被主动使用,只有调用getInstance方法时才会显示加载SingletonHolder类
      public class Singleton{
          private static class SingletonHolder{
              private static Singleton singleton = new Singleton();
          }
          private Singleton(){}
          public static Singleton getInstance(){
              return SingletonHolder.singleton;
          }
      }
                 
      注: 以上的实现方法都有两个共同缺点;都需要额外的工作来实现序列化; 可以使用反射强行调用私有构造器(可以让构造器在创建第二的实例时抛异常).
    5. 枚举

      这种写法很特殊, 在实际工作中也很少见, 在Android平台是不被推荐的.

      但是线程安全和防止反射强行调用构造器, 还提供了自动序列化机制.

      public enum Singleton{
          INSTANCE;
          public void whateverMethod(){
          }
      }
                 
    6. 双重检验锁

      在jdk1.5之后就有了双重校验锁, 这是我觉得最好的一个方法.
      public class Singleton{
          private static Singleton singleton;
          private Singleton(){}
          public static Singleton getInstance(){
              if(singleton==null){
                  synchronized(Singleton.class){
                      if(singleton==null){
                          singleton = new Singleton();
                      }
                  }
              }
              return singleton;
          }
      }
                 
  • 牢记

    不管采用什么方式实现, 都要牢记单例的三大要点:
    • 线程安全

    • 延迟加载

    • 序列化与反序列化安全