vlambda博客
学习文章列表

大话设计模式之单例模式


    俗话说:"天下大势;合久必分,分久必合",中华五千年的历史文化,无数朝代的更迭,一次又一次的证明了这条规律的正确性;既然是规律那就一定不止在“天下大势” 方面出现,在很多其他的事物上也同样有所体现。

    比如,我们缩小一个单位量级,放在一个国家的层面来说,一个国家只能同时有一个皇帝,并且皇帝这个职位,也是''风水轮流转,今年到我家'';而且每隔几年总有那么几个皇帝干着干着不想干撂挑子了或者干脆被别人不想干了(被别人抢了皇位);但是无论怎样,我们都能发现,当皇帝的人总是只有一个。正所谓:"一山不容二虎",否则会出大事的。

    历史上倒还真出现过有两个皇帝的时刻,那就是大明王朝;这种情况就出现了国家安全的问题,并不可取。


大话设计模式之单例模式







单例模式三步曲

    如果把皇帝这个职业的唯一性的特点,放到我们的软件开发行业的话,同样也有相通的地方,比如:在设计模式中就有单例模式,那么何为单例模式呢:


单例模式:保证整个系统中一个类只有一个对象的实例,实现这种功能的方式就叫单例模式。


    翻译成白话文就是,整个系统中有且只有一个实例对象,这样在我们的程序中就可以共享这个对象资源了,这也是设计模式中最简单的一种设计模式,今天我们就来唠唠单例模式。

    刚才从概念中,我们了解到了单例模式就是整个系统中只有一个实例对象,那么问题来了,怎么才能保证整个系统中只有一个对象实例呢?这就是接下来我们重点要探讨的。

    要保证整个系统中对象只有一个,这里就要借鉴宋丹丹老师当年把大象装冰箱总结出的三部曲,所以保证对象的唯一性也只需要三步。


大话设计模式之单例模式




  1. 私有化构造器

    既然对象只有一个,那就不能随便什么人都能创建对象了,所以这里要严格把控。

  2. 自己创建对象

    既然不让别人创建对象,那就只能自己实例化来创建对象了。

  3. 提供获取对象的方法

    因为对象是自己创建的,所以我们还的提供一个方法其他人能够拿到来使用。








单例模式实现

    上面咱么讨论了单例模式的实现方式,三步走;这里我们就要将理论转化为实践;毕竟,实践是检验真理的唯一标准;接下来上代码。







单例模式之饿汉式

    听到饿汉式这个名字,大家可能都可以想到一个饥肠辘辘的饿汉,这个时候他最需要的就是可以马上有东西吃,所以单例模式的饿汉式就是在类刚被加载的时候,马上就创建对象,生怕抢不到吃的。


大话设计模式之单例模式


/**
 * 单例模式之饿汉式
 */
public class SingletonHungry {
 
  // 在类加载的时候,就创建对象
  private final static SingletonHungry instance = new SingletonHungry();
 
  /**
   * 私有化构造器,防止外界创建对象
   */
  private SingletonHungry() {
  }
    
  // 提供外界获取对象的方法
  public static SingletonHungry getInstance() {
    // 返回单利对象
    return instance;
  }
}

    

    饿汉式虽好,但是总感觉还是有点问题,因为它在类刚被加载的时候,就各种创建对象,如果我暂时还不需要的话,这样其实是会造成资源的浪费的,所以还不完美,怎么来解决这个问题呢;大家应该经常听到懒加载这个词,它的意思就是所有的事情我都不着急,只有你需要我的时候我才出现,这个模式就是懒汉式。







单例模式之懒汉式

    懒汉式的方式就是,一开始比较懒,不去创建对象,等到程序需要我的时候,实在没法再拖了,就只能创建对象了。


大话设计模式之单例模式


/**
 * 单例模式之懒汉式
 */
public class SingletonLazy {
 
  // 懒汉式 需要对象的时候再去创建
  private static final SingletonLazy instance = null;
 
 
  // 私有化话构造器
  private SingletonLazy() {
  }
 
  /**
   * 对外暴露的获取单例对象的方法,
   * 存在多线程并发不安全的问题,可以给通过加锁synchronized关键字 锁会影响性能
   *
   * @return
   */
  public SingletonLazy getInstance() {
    // 判断是否已经创建了对象,没有就创建,有,就直接返回
    if (instance == null) {
      return new SingletonLazy();
    }
    return instance;
  }
}

    

    这种方式看着不错,不会有饿汉式的浪费资源;但却带来了另外一个问题,线程安全问题,这个问题在互联网环境下的高并发的情况下,可能比资源浪费还要严重,对于多线程并发带来的安全问题,我们可以使用加锁synchronized关键字来解决。


/**
 * 单例模式之懒汉式加锁解决线程安全问题
 */
public class SingletonLazy {
 
  // 懒汉式 需要对象的时候再去创建
  private static final SingletonLazy instance = null;
 
 
  // 私有化话构造器
  private SingletonLazy() {
  }
 
  /**
   * 对外暴露的获取单例对象的方法
   * @return
   */
  public synchronized SingletonLazy  getInstance() {
    // 判断是否已经创建了对象,没有就创建,有,就直接返回
    if (instance == null) {
      return new SingletonLazy();
    }
    return instance;
  }
}

    

    到这里,线程安全问题也解决了,但是由于加锁又带来了性能的问题;真是一波未平一波又起啊,性能问题在也是极其重要的问题,这里我们就要换一个思路了。







单例模式之静态内部类

这里我们用静态内部类的方式来实现单例模式


/**
 * 静态内部类写法
 *  *  推荐写法
 */
public class Singleton {
 
  private Singleton() {
  }
  /**
   * 静态内部类 只在getInstance方法内使用
   */
  private static class SingletonHolder {
    public static Singleton instance = new Singleton();
  }
 
  // 返回接口
  public static Singleton getInstance() {
    return SingletonHolder.instance;
  }
}

    

    这种方式跟饿汉式方式采用的机制类似,但又有不同;两者都是采用了类加载的机制来保证初始化实例时只有一个线程。


不同:

  • 饿汉式是只要Singleton类被加载就会实例化,没有懒加载

  • 静态内部类方式是在需要实例化时,调用getInstance方法,才会加载SingletonHolder类,实例化Singleton


    由于类的静态属性只会在第一次加载类的时候初始化,所以在这里我们也保证了线程的安全性,所以通过这种静态内部类的方式解决了资源浪费和性能的问题。

    静态资源内部类的方式也是我们比较推荐的一种写法哦。


End



AI课工场


长按关注