设计模式-简单的单例

设计模式-“简单”的单例

概要

一些场景下,针对一些类,我们往往只需要实例化出一个对象,这样的类我们称为单例类,创建的实例对象,称之为单例对象,比如操作系统中的任务管理器,展示系统资源使用情况,如果此时存在多个对象,还需要保持多个对象之间数据一致性,反而使得程序处理变得繁琐。类似的,还有线程池、缓存、对话框、注册表、设备驱动程序对象等,也只需要实例化一次。
单单从类结构来看,单例模式也许是最简单的设计模式,但深入到细节,还是有些考究点的,其类结构图入下图所示:
单例模式结构图

单例模式保证单例类最多被实例化一次,并提供一个全局访问点。


实现

饿汉式

比较直接、保险的方式是将构造方法私有化,内部构建出实例对象,提供对外的静态访问方法获取单例对象,示例代码如下:

package singleton;

public class EagerSingleton {
    private static EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return instance;
    }

}

可以看出,内部实例对象,在类初始化时进行了创建,并且单例类提供了静态访问方法getInstance()供外部获取对象。饿汉式实现方式也称为急切实例化,此种实现方式适用于应用程序总是访问实例对象或对象在创建、运行时不占用太多资源的场景。

懒汉式

对应的,在需要延迟实例化(lazy instantiaze)的情景下,我们仅需要在第一次访问单例对象的时候实例化一次,后面再访问时,直接返回单例对象即可,这样便可防止了在系统不需要访问对象时创建多余对象的问题。直接想到的实现方式如下:

package singleton;

public class LazySingleton {
    private static LazySingleton instance = null;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

此时,在多线程环境下碰到的问题是同时执行到if判断语句时,创建出多个对象,无法保证单例。进一步,考虑在方法上添加synchronized关键字,同步方法执行。

package singleton;

public class LazySingleton {
    private static LazySingleton instance = null;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

双重检查锁定

synchronized关键字在保证线程同步的情况下,也带来了多线程情况下获取对象实例时的效率低下,进一步改进:将同步语句块缩小范围。如果将整个if判断语句块保持同步,每次获取对象时,都需要保持同步,进一步缩小同步范围,采用双重检查锁定的实现方式改进如下:

package singleton;

public class LazySingleton {
    private volatile static LazySingleton instance = null;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

注意:以上实现方式都存在通过反射机制调用私有构造方法的风险,可进一步考虑在私有构造方法中添加验证实例个数的判断

枚举实现方式

也许是最简单的单例实现方法:

package singleton;

public enum Singleton {
    INSTANCE;
}

它简洁的规避了多次实例化的风险,无偿的提供了序列化机制,《Effective Java》甚至说这种方式已经成为实现Singleton的最佳方法。


小结

  1. 单例模式保证单例类对象的唯一性,有其具体的应用场景,虽说有多种实现方式,但理清了它们的优缺点时,不会再被这些名词弄晕;
  2. 虽然单例模式的结构简单,但要保证实例化一个对象也不简单,涉及到的基础知识也很多:序列化时的唯一性保证、volatile在多线程编程中的作用、类加载器的限制等,小编也在不断积累中。

参考资料

《大话设计模式》
《Head First 设计模式》
《Effective Java》
《设计模式Java版》
【如何正确地写出单例模式】

PS:CSDN中的单例

在写这篇文章时,被CSDN中的单例应用狠狠的折磨了一番。在使用CSDN中markdown编辑器写到文章结尾的时候,同一个浏览器下手残的去开了CSDN的另一个markdown编辑器页面,这时再去打开原来的编辑页面,之前没有保存草稿的内容消失了……不得不重写一遍/(ㄒoㄒ)/~~ @CSDN,这是不是Bug呀???

发布了159 篇原创文章 · 获赞 225 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/lyg673770712/article/details/79635282