单件模式 (Singleton design pattern)

有些对象我们只需要一个,比如:线程池、缓存、对话框、处理偏置爱好设置和注册表的对象、
日志对象、充当打印机、显卡等设备驱动程序的对象。
事实上,这些对象只能有一个实例,如果制造出更多的实例,就会导致许多问题产生,
例如:程序的行为异常、资源使用过量、或者是不一致的结果。

单件模式可以确保只有一个实例被创建。
单件模式也给我们一个全局访问点,和全局变量一样,不同的是全局变量会在程序一开始就被创建出来,
如果这个对象非常消耗资源,而程序在这次的执行过程中又一直没有用到它就是浪费,而利用单例模式,
我们可以在需要的时候才创建对象。

1 Eager initialization

如果应用程序总是需要创建并使用单间实例,或者在创建和运行时方面的负担不会太繁重,你可以使用
急切(eagerly)创建此单件,如下所示:

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

    // private constructor
    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return instance;
    }
}

急切实例化能很好地工作,但是它有一个弊端。实例会被马上创建而不管是否程序在运行时是否需要用到它,
如果这是一个相对大的对象那么这就是一种更浪费。
下面的方法(Lazy initialization)能很好地解决这个问题。

2 Lazy initialization

在程序设计中,延迟实例化是一种延迟——创建对象、计算数值、还有其他开销大的操作知道我们第一次真的需要它的时候
的策略,在单件模式中,它限制实例的创建直到程序第一次请求它的时候。让我们来看看代码实现

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

    // private constructor
    private LazySingleton() {
    }

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

一旦方法LazySingleton.getInstance()被调用,上面的逻辑会先检查实例存在与否,
如果不存在(instance == null)就创建一个实例,然后返回这个实例;
如果实例已经存在就返回这个实例。

不过在多线程中使用这个方法会遇到问题
考虑两个线程以下列顺序调用了LazySingleton.getInstance()
这里写图片描述
这会导致在我们的程序中产生两个LazySingleton对象,这就会导致程序出错。

用双重检查(double-checked locking)可以解决这个错误.
首先检查实例是否已经创建,如果尚未创建才进入同步区块,并且会再次检查实例是否已经创建,实现代码:

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

    // private constructor
    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        if (instance == null) {
            synchronized (EagerSingleton.class) {
                // Double check
                if (instance == null) {
                    instance = new EagerSingleton();
                }
            }
        }
        return instance;
    }
}

以上就是一个单件模式的正确的实现。

注意:

  1. 这里确保使用了”volatile”关键字,否则会导致一个(out of order write error),
    会使得对这个实例的引用在对象还没有被创建之间返回(JVM 仅仅分配了相应内存但是构造方法还没有被调用执行)。
    在这种情形下,你的另一个线程使用这个对象的实例引用时,会出现null pointer exception甚至导致整个程序崩溃。
  2. 同时,要注意双重检查不适用于java-1.4及更早版本,这些版本的JVM对于volatile关键字的实现会导致双重检查失效。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值