• 最简单的设计模式--单例模式


      我看的设计模式书是《Head First设计模式》,我决定不按照书上的章节顺序做笔记,按照我认为的容易理解程度从易到难来写。

      一、单例模式的定义

      单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。

      二、实例说明

      有一些对象其实我们只需要一个,例如:线程池(threadpool)、缓存(cache)、对话框、处理器偏好设置和注册表(registry)的对象、日志对象等。

      2.1 一个最简单的经典单例模式代码

      我们可以用Java的静态变量来坐到这一点,但事静态全局变量在程序一开始就被创建好对象,如果这个对象非常耗费资源,而程序在这次执行过程中又一次没用到它,就形成浪费。

    经典的单例模式代码:

     1 public class Singleton {
     2     private static Singleton uniqueInstance;
     3     private Singleton(){}//构造器声明为私有,只有类内才可以调用构造器
     4     public static Singleton getInstance(){
     5         if(uniqueInstance==null){
     6             uniqueInstance=new Singleton();
     7         }
     8         return uniqueInstance;
     9     }
    10 }

      别的类用要用这个类的对象的话,就通过Singleton.getInstance()来获取,if判断而且确保了这个类只有一个实例化的对象。这样似乎都一切正常了。不过实际项目中,肯定会有多线程的场景,那样就可能产出两个实例。例如这里有两个线程,线程1运行到上述代码第5行时候,new 一个对象,假如此刻线程2也进入5行,发现此时uniqueInstance为null,它也new 一个对象,那么就产生了两个实例化对象了。所以,要在第4行上面加上synchronized,这样就保证了不会存在两个线程同时进入到getInstance方法。

      2.2改善多线程

      在2.1中我们说过在getInstance方法加入synchronized关键字来解决多线程会实例化多个对象的问题,它也存在一些问题:

      1)同步会降低性能;

      2)更严重的是:上述代码只要第一次执行getInstance方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。

      下面给出解决三种解决方案:

      2.2.1 如果getInstance()的性能对应用程序不是很关键,就什么都别做

       没错,就是这么直接!当然,如果getInstance()在程序中频繁的运行,那就得重新设计了。

      2.2.2 使用“急切”创建实例,而不用延迟实例化的做法

      如果程序总是会创建并使用到这个单例类,或者创建这个单例类的实例不繁重,可以急切的创建此单例:

    public class Singleton {
        private static Singleton uniqueInstance=new Singleton();//在静态初始化中创建单例
        private Singleton(){}
        public static Singleton getInstance(){
            return uniqueInstance;
        }
    }

      2.2.3 用“双重检查加锁”,在getInstance()中减少使用同步

      首先检查是否实例已经创建了,如果没有,“才”进行同步。这样一来,只有第一次创建会用到同步,这正是我们想要的。

     1 public class Singleton {
     2     private volatile static Singleton uniqueInstance;
     3     private Singleton() {}
     4     public static Singleton getInstance() {
     5         if (uniqueInstance == null) {
     6             synchronized (Singleton.class){
     7                 if(uniqueInstance == null){//进入区域后,再检查一次,如果仍是null,才创建实例
     8                     uniqueInstance = new Singleton();
     9                 }
    10             }
    11         }
    12         return uniqueInstance;
    13     }
    14 }

      第7行还需要加入一次判断,有可能别的线程在此线程拿5-6行的期间,已经实例化了。(ps:我的个人理解)关于volatile关键字,不清楚的同学可以去查,以后等我看完JVM,会专门写一篇volatile关键字的文章。

      三、单例模式的延伸

      1、听说两个类加载器可能会各自创建自己的单例对象?

      答:是的。每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会同时加载同一个类,从整个程序来看,同一个类会被加载多次。如果这样的事情发生在单件上,就会产生多个单例并存的现象。所以,如果程序有个多个类加载器又同时使用了单例模式,可以这样解决:自行指定类加载器,并指定同一个类加载器。

      2、全局变量比单例模式差在哪里?

      答:1)不可延迟实例化;2)并不能确保只有一个实例,而且也变相鼓励开发人员,用许多全局变量指向许多小对象来造成命名空间的污染。

  • 相关阅读:
    牛客练习赛64 D.宝石装箱 【容斥原理+背包DP】
    洛谷 P5212 SubString【SAM+LCT】
    洛谷 P4219 [BJOI2014]大融合【LCT】
    洛谷 P1501 [国家集训队]Tree II【LCT】
    洛谷 P5357 【模板】AC自动机(二次加强版)
    洛谷 P3690 【模板】Link Cut Tree (动态树)
    洛谷 P2463 [SDOI2008]Sandy的卡片【后缀数组】
    P3181 [HAOI2016]找相同字符【后缀数组】
    洛谷 SP705 【后缀数组】
    牛客小白月赛18 E.Forsaken的数列【Splay】
  • 原文地址:https://www.cnblogs.com/chiclee/p/6475864.html
Copyright © 2020-2023  润新知