设计模式学习笔记—单例模式

对于一个单例来说,应该注意的大概只有下面一点。

确保程序从始至终只能操纵一个对象。

首先需要考虑的是一个单例必须要注意的问题。

1.保证这个类只能被创建一次。
2.整个程序只能访问到一个对象。

基于这几点可以设计出这样的一个类。

Singleton.java

public class Singleton {
    // 静态变量保证应用程序只有一个变量
    private static Singleton mInstance;

    // 私有化构造方法保证对象只能在这个类内部被创建
    private Singleton() {
        ...
    }

    // 为其它类提供一个可以获得这个变量的公共方法
    public static Singleton getInstance() {
        return mInstance;
    }
}

接下来需要考虑的就是何时去创建这样一个对象,一般来说常用的有两种方法“懒汉式(Lazy initialization)”和“饿汉式(Eager initialization)”。
“懒汉式”就是在这个对象第一次被使用的时候进行创建,而“饿汉式”就是在类进行初始化的时候就创建”,这两种方法的加载分别会导致两种不同的问题。

“懒汉式”改动的代码

    public static Singleton getInstance() {
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        return mInstance;
    }

导致的问题就是,当两个线程同时去获得这个单例时,有可能会获得两个不同的对象,这种做法是线程不安全的,不考虑执行效率问题的情况下,可以添加 synchronized 来保证这个方法同时只有一个线程在访问,更改后的代码如下

    public static synchronized Singleton getInstance() {
        ...
    }

“饿汉式”改动的代码

    private static final Singleton mInstance = new Singleton();

看起来这种方法简单易懂,也不用考虑线程安全问题,但如果程序中任何以这种方式创建的静态变量过多,或者单例构造方法中有比较耗时的操作,则这个程序的启动时间就会变长,而且这个对象无论有没有被使用都会被创建,算是一种资源上的浪费。然而最大的问题是有些情况下无法使用“饿汉式”,比如单例的构造方法中有参数。

所以在保证线程安全的情况下,“懒汉式”比“饿汉式”节省程序启动时间与对象的所占用的内存。“饿汉式”比“懒汉式”节省第一次使用单例时所消耗的时间。
然而,这两种方式并没有什么大的区别,个人感觉“懒汉式”适用范围更广一些,因为有好多事情可能需要在程序启动的时候要做,单例对象的话,能省点时间是一点,况且用户在使用时感觉偶尔的一次卡顿,也许是系统卡了一下也说不定。(๑•́ ₃ •̀๑)

最后,说明一下上面那种“懒汉式”的执行效率问题,线程同步是很费时间的,所以,单例类最好写成这样

public class Singleton {
    private static volatile Singleton mInstance;

    private Singleton() {
        ...
    }

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

这个处理方式的话叫做“双重检查锁(double-checked locking)”,确保了线程安全的前提下又提升了执行效率,然而会发现一个并不认识的关键字 volatile ,这个关键字的作用是防止下面这种情况发生。

线程1 首次访问 getInstance 方法,mInstance 为空, 将 mInstance 对象指向分配的内存空间,同时,线程2 访问,mInstance 不为空,线程2 获得 mInstance 对象的复制, 由于线程锁的问题所以线程2 需要等待线程1 构造方法执行完毕,线程2调用 mInstance 的方法,但由于是复制的时候线程1 还没有把真正的数据写入主内存中,所以线程2 获得的对象实际内存内容为空,然后报错。o(^▽^)o~♪
(以上内容由于是我自己编的,而且很难进行测试,所以看看就好。)

volatile 的作用,就是跳过线程对对象的复制,直接对主内存进行操作(大概),保证线程安全,反正单例的话这样写没错就对了(1.5之前的java应该没人用了吧)。

参考资料

Singleton pattern
HeadFirst设计模式
单例模式、双检测锁定DCL、volatile

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值