zoukankan      html  css  js  c++  java
  • 一、只有一个实例的设计模式:单例模式

    单例模式应该是设计模式中最常见的。正常创建对象的时候是通过new 构造方法(参数) 来实例出一个对象的,但是这样会导致,如果是一个通用的对象,反复创建的话会浪不必费要的内存,因此在这个时候可以使用单例模式来获取对象唯一的实例。

    为什么要使用单例模式

    Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。一些资源管理器常常设计成单例模式。

    外部资源:譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干个通信端口,系统应当集中管理这些通信端口,以避免一个通信端口被两个请求同时调用。

    内部资源,譬如,大多数的软件都有一个(甚至多个)属性文件存放系统配置。这样的系统应当由一个对象来管理这些属性文件。

    使用单例模式的好处:

    • 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
    • 由于new操作的次数减少。因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间

    单例模式的优点:

    • 单例类只有一个实例
    • 共享资源,全局使用
    • 节省创建时间,提高性能。

    如何创建一个单例模式

    最主要的就是保证,构造器私有化,这样就无法通过new来实例出对象,另外提供一个访问它的全局访问点。

    在Java语言中,有两种构建方式:

    • 饿汉式:在程序启动或单件模式类被加载的时候,单件模式实例就已经被创建
    • 懒汉式:当程序第一次访问单件模式实例时才进行创建

    单例模式的几种写法

    • 1.饿汉式

    这种方式和名字很贴切,饥不择食,在类装载的时候就创建,不管你用不用,先创建了再说,如果一直没有被使用,便浪费了空间,典型的空间换时间,每次调用的时候,就不需要再判断,节省了运行时间。

    public class Singleton {  
         private static Singleton instance = new Singleton();  
         private Singleton (){
         }
         public static Singleton getInstance() {  
    	     return instance;  
         }  
     }
    • 2.懒汉式(非线程安全)

    懒汉模式申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且在多线程不能正常工作。在多线程访问的时候,很可能会造成多次实例化,就不再是单例了。

    public class Singleton {  
          private static Singleton instance;  
          private Singleton (){
          }   
          public static Singleton getInstance() {  
    	      if (instance == null) {  
    	          instance = new Singleton();  
    	      }  
    	      return instance;  
          }  
     }
    • 3.懒汉式(线程安全)

    这两种「懒汉式」单例,名字起的也很贴切,一直等到对象实例化的时候才会创建,确实够懒,不用鞭子抽就不知道走了,典型的时间换空间,每次获取实例的时候才会判断,看是否需要创建,浪费判断时间,如果一直没有被使用,就不会被创建,节省空间。

    因为这种方式在getInstance()方法上加了同步锁,所以在多线程情况下会造成线程阻塞,把大量的线程锁在外面,只有一个线程执行完毕才会执行下一个线程。

    public class Singleton {  
          private static Singleton instance;  
          private Singleton (){
          }
          public static synchronized Singleton getInstance() {  
    	      if (instance == null) {  
    	          instance = new Singleton();  
    	      }  
    	      return instance;  
          }  
     }
    • 4.双重校验锁(DCL)

    这种写法在getSingleton()方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。在这里用到了volatile关键字,不了解volatile关键字的可以查看 Java多线程(三)volatile域 和 java中volatile关键字的含义 两篇文章,可以看到双重检查模式是正确使用volatile关键字的场景之一。

    「双重校验锁」:既可以达到线程安全,也可以使性能不受很大的影响,换句话说在保证线程安全的前提下,既节省空间也节省了时间,集合了「饿汉式」和两种「懒汉式」的优点,取其精华,去其槽粕。

    public class Singleton {
    
    	/**
         * 注意此处使用的关键字 volatile,
         * 被volatile修饰的变量的值,将不会被本地线程缓存,
         * 所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
         */
        private volatile static Singleton singleton;
        private Singleton() {
        }
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized(Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }
    • 5.静态内部类

    第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化sInstance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。

    public class Singleton { 
        private Singleton(){
        }
          public static Singleton getInstance(){  
            return SingletonHolder.sInstance;  
        }  
        private static class SingletonHolder {  
            private static final Singleton sInstance = new Singleton();  
        }  
    }
    • 6.枚举

    枚举单例的优点就是简单,但是大部分应用开发很少用枚举,可读性并不是很高,不建议用。

    public enum Singleton {
    	 //定义一个枚举的元素,它就是 Singleton 的一个实例
         INSTANCE;  
         
         public void doSomeThing() {  
    	     // do something...
         }  
     }
    • 7.使用容器

    这种事用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

    public class SingletonManager { 
      private static Map<String, Object> objMap = new HashMap<String,Object>();
      private Singleton() { 
      }
      public static void registerService(String key, Objectinstance) {
        if (!objMap.containsKey(key) ) {
          objMap.put(key, instance) ;
        }
      }
      public static ObjectgetService(String key) {
        return objMap.get(key) ;
      }
    }

    面试时候可能会问到的问题

    1. 什么是单例模式。
    2. 懒汉和饿汉模式之间的区别
    3. 手写一个单例模式
    4. 单例模式的优点
    5. 列举几种常用的设计模式

    上面很多东西,有的是从别的文档里面找的,有的是一些个人的理解,当然了有可能理解有什么误解,也希望能够原谅并及时指出。

    参考文档:https://itimetraveler.github.io/2016/09/08/%E3%80%90Java%E3%80%91%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/#%E3%80%90%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99%E3%80%91%EF%BC%9A

  • 相关阅读:
    [css] BFC规则以及解决方法
    [css] 利用border制作三角型
    初学java注解编程 记录错误及解决办法
    springmvc 注解 配置文件解释
    log4net详解(转载)
    数据库的隔离级别
    Arcgis 几何网络分析
    Spring配置文件详解 – applicationContext.xml文件路径
    wpf 线程
    wpf 保存控件中的内容为图片格式
  • 原文地址:https://www.cnblogs.com/shenyanrushang/p/10852486.html
Copyright © 2011-2022 走看看