从一生所爱到单例模式

Posted 苍南竹竿君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从一生所爱到单例模式相关的知识,希望对你有一定的参考价值。

概要

好无聊,无聊到只能用学习来打发时间了。
刚刚听音乐,正好放到了一首我非常喜欢的歌 一生所爱。就是大话西游里面的那首。每每听到这首歌,心,痛。
咦,好像一生所爱和我今天要总结的单例模式关联不上了,不管了,下面直接开始总结,不然我的心就白白痛了啵。


饿汉式

个人以为饿汉式是实现单例最简单粗暴等方式了。无论是谁,随便拿一本 Java 书籍,只要看过前 30 页,就能写饿汉式的单例。原因很简单只要知道静态变量在类加载的时候就被创建了,那么任意定义静态变量并直接初始化,你不想单例都难了。

 
   
   
 
  1. class SingletonHungry {

  2.    private static SingletonHungry instance = new SingletonHungry();

  3.    private SingletonHungry() {}

  4.    public static SingletonHungry getInstance() {

  5.        return instance;

  6.    }

  7. }

饿汉式的单例实现起来十分简单而且还是线程安全的,但是并没有延时初始化,这会对内存造成浪费,就像很多安卓手机应用,明明没用到,却在开机时候就在后台偷偷启动了。不过鉴于如今硬件升级速度之快,可以让我们不用那么关心这点内存问题。

懒汉式

懒汉式分两种,一种是线程不安全的,还有一种是线程安全的。
如下这段个单例在多线程环境下就有可能会出错。

 
   
   
 
  1. //懒汉式 线程不安全

  2. class SingletonLazy {

  3.    private static SingletonLazy instance;

  4.    private SingletonLazy() {}

  5.    public static SingletonLazy getInstance() {

  6.        if (null == instance) {

  7.            instance = new SingletonLazy();

  8.        }

  9.        return instance;

  10.    }

  11. }

多线程出错演示
接下来是线程安全的懒汉式

 
   
   
 
  1. //懒汉式 线程安全

  2. class SingletonLazySafe {

  3.    private static SingletonLazySafe instance;

  4.    private SingletonLazySafe() {}

  5.    public static synchronized SingletonLazySafe getInstance() {

  6.        if (null == instance) {

  7.            instance = new SingletonLazySafe();

  8.        }

  9.        return instance;

  10.    }

  11. }

看线程安全和不安全的两种实现方式代码其实没什么差别,唯一不同的地方就是线程安全的方式加了一个关键字 synchronized,用于确保 getInstance 方法线程安全。但是这样线程安全是安全了,那么如果接下来有上百上千个线程来访问这个方法,每个线程调用一次就把这个方法先锁一次,你说你死不死。所以这两种其实都不是实现单例的好方法。

双重校验模式 double-checked locking

说到底 双重校验模式 也是 懒汉式 的一种。它解决了上面懒汉式效率低下的问题。

 
   
   
 
  1. class SingletonDCL {

  2.    private static SingletonDCL instance;

  3.    private SingletonDCL() {}

  4.    public static SingletonDCL getInstance() {

  5.        if (null == instance) {

  6.            synchronized (SingletonDCL.class) {

  7.                if (null == instance) {

  8.                    instance = new SingletonDCL();

  9.                }

  10.            }

  11.        }

  12.        return instance;

  13.    }

  14. }

可以从代码中看出,我们学聪明了,没有将整个 getInstance 方法给锁住,而是只锁创建实例的部分。另外进行了两次 null 判断。
这两次判断还是比较讲究的,第一次判断是否为 null 的意图很明显,就是为了如果实例已经存在那么我们就直接返回实例,不要再做任何多余的操作。
第二次判断是为了解决多线程情况下创建多个实例的问题。(假设一下没有这个判断,当两个线程 A,B 同时访问这个方法,A 线程先获取了锁,B 线程在等待。A 线程完成实例化操作,释放了锁,然后 B 线程开始执行实例化操作,这个时候因为没有判断 instance 已经被实例化过了,又重新实例化一遍,这就不是单例了。)
所以,双重校验模式才是真正的懒汉式,嗯!

登记式/静态内部类

使用静态内部类方式能达到双重校验模式一样的效果,而且实现更加简单。
这里用到了 Java 静态内部类的特性,加载类的时候,不会加载静态内部类,只有在使用的时候才会进行显示装载。

 
   
   
 
  1. class SingletonRegister {

  2.    private static class SingletonInner {

  3.        private static final SingletonRegister INSTANCE = new SingletonRegister();

  4.    }

  5.    private SingletonRegister() {

  6.    public static SingletonRegister getInstance() {

  7.        return SingletonInner.INSTANCE;

  8.    }

  9. }

枚举方式

说实话,从没有在实际项目中看到用枚举方式实现单例的,不过这是 Effective Java 作者 Josh Bloch 提倡的方式。
这种方式实现起来简单的让人疑惑 这是单例吗?没错,还真是。

 
   
   
 
  1. enum SingletonEnum {

  2.    INSTANCE("url", "name", "pwd", "etc");

  3.    private String url;

  4.    private String name;

  5.    private String pwd;

  6.    private String etc;

  7.    SingletonEnum(String url, String name, String pwd, String etc) {

  8.        this.url = url;

  9.        this.name = name;

  10.        this.pwd = pwd;

  11.        this.etc = etc;

  12.    }

  13.    public void doSomething() {

  14.        System.out.println("do something");

  15.    }

  16.    public SingletonEnum getInstance() {

  17.        return INSTANCE;

  18.    }

  19. }


## 总结一般在实际项目中使用到单例模式时候,会以饿汉模式作为首选,如果有延时加载要求的那才会用内部类实现方式。如果有反序列化需求的,那么可以用枚举方式实现,毕竟 Enum 已经实现了 Serializable 接口。

总结好了,老朽要再去刷遍《大话西游》了。


以上是关于从一生所爱到单例模式的主要内容,如果未能解决你的问题,请参考以下文章

面试时被问到单例模式,怎么回答才能让面试官眼前一亮?

Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量

Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量

Singleton模式(单例模式)

健身3年,再看坚持的意义!如何找到你的一生所爱?

设计模式之单例设计模式