欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

下面继续忽略反 昆山软件开发 射和序列化的问题

点击: 次  来源:宝鼎软件 时间:2017-10-26

原文出处: 猴子007

“你知道茴香豆的‘茴’字有几种写法吗?”

纠结单例模式有几种写法有用吗?有点用,口试中常常选择个中一种或几种写法作为话头,观察设计模式和coding style的同时,还很容易扩展到其他问题。这里讲授几种猴哥常用的写法,但切忌生搬硬套,去记“茴香豆的写法”。编程最大的兴趣在于“know everything, control everything”。

概略可分为4类,下面别离先容他们的根基形式、变种及特点。

饱汉模式

饱汉是变种最多的单例模式。我们从饱汉出发,通过其变种逐渐相识实现单例模式时需要存眷的问题。

基本的饱汉

饱汉,即已经吃饱,不着急再吃,软件开发,饿的时候再吃。所以他就先不初始化单例,等级一次利用的时候再初始化,即“懒加载”。

// 饱汉
// UnThreadSafe
public class Singleton1 {
  private static Singleton1 singleton = null;
  private Singleton1() {
  }
  public static Singleton1 getInstance() {
    if (singleton == null) {
      singleton = new Singleton1();
    }
    return singleton;
  }

饱汉模式的焦点就是懒加载。长处是更启动速度快、节减资源,一直到实例被第一次会见,才需要初始化单例;小弊端是写起来贫苦,大弊端是线程不安详,if语句存在竞态条件。

写起来贫苦不是大问题,可读性好啊。因此,单线程情况下,基本饱汉是猴哥最喜欢的写法。但多线程情况下,基本饱汉就彻底不行用了。下面的几种变种都在试图办理基本饱汉线程不安详的问题。

饱汉 – 变种 1

最粗暴的犯罪是用synchronized要害字修饰getInstance()要领,这样能到达绝对的线程安详。

// 饱汉
// ThreadSafe
public class Singleton1_1 {
  private static Singleton1_1 singleton = null;
  private Singleton1_1() {
  }
  public synchronized static Singleton1_1 getInstance() {
    if (singleton == null) {
      singleton = new Singleton1_1();
    }
    return singleton;
  }
}

变种1的长处是写起来简朴,且绝对线程安详;弊端是并发机能极差,事实上完全退化到了串行。单例只需要初始化一次,但就算初始化今后,synchronized的锁也无法避开,从而getInstance()完全酿成了串行操纵。机能不敏感的场景发起利用。

饱汉 – 变种 2

变种2是“污名昭著”的DCL 1.0。

针对变种1中单例初始化后锁仍然无法避开的问题,劳务派遣管理系统,变种2在变种1的外层又套了一层check,加上synchronized内层的check,即所谓“双重查抄锁”(Double Check Lock,简称DCL)。

// 饱汉
// UnThreadSafe
public class Singleton1_2 {
  private static Singleton1_2 singleton = null;
  private Singleton1_2() {
  }
  public static Singleton1_2 getInstance() {
    // may get half object
    if (singleton == null) {
      synchronized (Singleton1_2.class) {
        if (singleton == null) {
          singleton = new Singleton1_2();
        }
      }
    }
    return singleton;
  }
}

变种2的焦点是DCL,看起来变种2好像已经到达了抱负的结果:懒加载+线程安详。惋惜的是,正如注释中所说,DCL仍然是线程不安详的,由于指令重排序,你大概会获得“半个工具”。具体在看完变种3后,可参考猴子之前的一篇文章,这里不再赘述。

参考:volatile要害字的浸染、道理

饱汉 – 变种 3

变种3专门针对变种2,可谓DCL 2.0。

针对变种3的“半个工具”问题,变种3在instance上增加了volatile要害字,道理见上述参考。

// 饱汉
// ThreadSafe
public class Singleton1_3 {
  private static volatile Singleton1_3 singleton = null;
  private Singleton1_3() {
  }
  public static Singleton1_3 getInstance() {
    if (singleton == null) {
      synchronized (Singleton1_3.class) {
        // must be a complete instance
        if (singleton == null) {
          singleton = new Singleton1_3();
        }
      }
    }
    return singleton;
  }
}

多线程情况下,变种3更合用于机能敏感的场景。但后头我们将相识到,就算是线程安详的,尚有一些步伐能粉碎单例。

饿汉模式

与饱汉相对,饿汉很饿,只想着尽早吃到。所以他就在最早的机缘,即类加载时初始化单例,劳务派遣管理系统,今后会见时直接返回即可。

// 饿汉
// ThreadSafe
public class Singleton2 {
  private static final Singleton2 singleton = new Singleton2();
  private Singleton2() {
  }
  public static Singleton2 getInstance() {
    return singleton;
  }
}

饿汉的长处是天生的线程安详(得益于类加载机制),写起来超等简朴,利用时没有延迟;弊端是有大概造成资源挥霍(假如类加载后就一直不利用单例的话)。

值得留意的时,单线程情况下,饿汉与饱汉在机能上没什么不同;但多线程情况下,由于饱汉需要加锁,饿汉的机能反而更优。

Holder模式