java 单例设计模式

发表于:2017-8-22 10:02

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:bsqql123    来源:51Testing软件测试网采编

  单例是应用开发中一种设计模式,主要应用场景为:当且仅当系统中只能保留一个对象时使用。本文提出4中可以在生产环境中使用的单例设计模式。推荐使用enum的方式。
  应用场景
  例如一下应用场景[1]:
  1、 Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗?
  2、网站的浏览人数统计,一般也是采用单例模式实现,否则难以同步。
  3、应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  //todo
  在joshua block 的《effective java second edition》 一书中给出了三种单例设计模式
  1、采用静态变量:
  public class TaskManager {
          public static final TaskManager INSTANCE = new TaskManager ();
          private TaskManager (){}
          //...
  }
  这种写法使用了私有的构造方法。来保证只能有一个实例,但是这种方法也有例外情况,因为,你可以通过反射来调用私有构造方法。这个时候你可以抛出异常。以下代码仅作为参考。
  public class TaskManager {
      public static final TaskManager INSTANCE = new TaskManager();
      private TaskManager() {
          if (INSTANCE != null) {
              try {
                  throw new Exception("An object already exists");
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      //...
  }
  2、采用静态方法
  public class TaskManager {
      private static final TaskManager INSTANCE = new TaskManager();
      private TaskManager() {}
      public static TaskManager getINSTANCE() {
          return INSTANCE;
      }
      //...
  }
  3、采用enum的方式
  这种模式是目前最佳的,因为:
  1、JVM会保证enum不能被反射并且构造器方法只执行一次。
  2、此方法无偿提供了序列化机制,绝对防止反序列化时多次实例化。
  3、运行时(compile-time )创建对象(懒加载) // todo 关于cmpile-time和run-time有时间我单独写一篇文章
  enum是jdk5的特性,现在(2017)web应用普遍在jdk6、7、8,所以可以放心使用。
  目前最佳的方式是使用接口的方式(解耦):
  interface Resource {
      Object doSomething();
  }
  public enum SomeThing implements Resource {
      INSTANCE {
          @Override
          public Object doSomething() {
              return "I am a Singleton nstance";
          }
      };
  }
  class Demo {
      public static void main(String[] args) {
          System.out.println(SomeThing.INSTANCE.doSomething());
      }
  }
  或者不使用接口的形式
  public enum SomeThing {
      INSTANCE;
      public void doSomething() {
          System.out.println("INSTANCE = " + INSTANCE);
      }
  }
  class Demo {
      public static void main(String[] args) {
          SomeThing.INSTANCE.doSomething();
      }
  }
  也有人用其他的方式,我对这种方法持强烈反对,具体可以参考文献4,以下代码仅做参考
  class Resource {
  }
  public enum SomeThing {
      INSTANCE;
      private Resource instance;
      SomeThing() {
          instance = new Resource();
      }
      public Resource getInstance() {
          return instance;
      }
  }
  class Demo {
      public static void main(String[] args) {
          System.out.println(SomeThing.INSTANCE.getInstance());
      }
  }
  在其他文章中有提到“懒汉”、“恶汉”的名词,其实懒汉主要就是"懒"加载[注:指在使用时装载,不使用时不进行装载]
  有人提出这种懒汉设计
  public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}  
    
      public static Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
      }  
  }
  很显然这种设计线程不安全,一般不会使用。
  有人又提出了懒汉改进的方法,使其线程安全。
  public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}  
      public static synchronized Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
      }  
  } 
 
  这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,因为是重量级锁,效率很低。
  于是有人提出了双重校验锁机制,这个用的也比较多。
  下面代码就是用double checked locking 方法实现的单例,这里的getInstance()方法要检查两次,确保是否实例INSTANCE是否为null或者已经实例化了,这也是为什么叫double checked locking 模式。
  /**
  * Singleton pattern example with Double checked Locking
  */
  public class DoubleCheckedLockingSingleton{
       private volatile DoubleCheckedLockingSingleton INSTANCE;
   
       private DoubleCheckedLockingSingleton(){}
   
       public DoubleCheckedLockingSingleton getInstance(){
           if(INSTANCE == null){
              synchronized(DoubleCheckedLockingSingleton.class){
                  //double checking Singleton instance
                  if(INSTANCE == null){
                      INSTANCE = new DoubleCheckedLockingSingleton();
                  }
              }
           }
           return INSTANCE;
       }
  }
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号