设计模式-单例模式

单例(Singleton)模式

通常,对象通过在自身属性上执行任务来承担自己的职责,除了需要维护自身的一致性外,无需承担其他任何责任。然而,仍有一些对象承担了更多的职责,例如对真实世界的实体进行建模、协调工作或者对整个系统的状态进行建模。当系统的其他对象都依赖于特殊对象所承担的职责时,我们需要通过某种方式找到这个承担职责的对象。例如,可能需要找到一个代表指定机器的对象,或者是从数据库获取数据来创建自身的客户对象,又或者是初始化系统内存用以恢复的对象。

在某些场景,你需要找到一个承担职责的对象,并且这个对象是它所属类的唯一实例。例如,焰火工厂可能需要唯一的一个Factory对象。此时,可以使用单例模式,danl单例模式的英文为Singleton。

单例模式的意图是为了确保一个类有且仅有一个实例,并为它提供一个全局访问点。

 

单例模式机制

单例模式的机制比其意图更加容易记忆。解释如何保证一个类有且仅有一个实例,要比解释为何需要这种限制要简单的多。正如Design Patterns一书中把单例模式归类为“创建型”模式。当然,理解模式的方式是没有限制的,只要能帮助你的记忆、识别和运用这些模式。对于单例模式,他的意图说明了该单例对象承担了其他对象所要yila依赖的职责。

创建一个担当着独一无二角色的对象,有多种方式。但是,不管你如何创建一个单例对象,都必须确保其他开发人员不能创建该单例对象的新的实例。

设置一个单例类时,需要确定何时实例化该类的单例对象。一种做法是创建这个类的实例,并将它作为该类的静态成员变量。例如:

private static Singleton singleton = new Singleton();

这个类通过一个公共的getFactory()静态方法获得该类的唯一实例。

如果不希望提前创建单例实例,还可以在第一次需要该实例时,延迟初始化它。例如:

public static Singleton getSingleton() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}

无论哪种场景,单例模式都建议提供一个公共的静态方法去访问单例对象。如果该方法创建了一个对象,他就要保证只有一个实例可以被创建。

 

单例和线程

如果想在一个多线程环境下延迟初始化一个单例模型,必须小心谨慎地避免多个线程同时初始化该单例对象。在多线程环境下,无法保证在其他线程开始执行该方法时,当前xia线程已经完整地执行完该方法。zhek这可能出现两个线程同时初始化一个单例对象的情况。假设第一个线程发现该单例对象为null,紧接着第二个线程运行,也会发现该单例对象为null。然后两个线程都会对该单例对象进初始化。为了避免这种竞争,需要用锁机制去协调不同线程对同一方法的执行。

Java支持多线程的开发。他可以为每个对象提供一个锁,用于表示对象是否已经被线程所占用。为确保仅有一个线程初始化单例对象,可以通过对适当的对象进行加锁同步初始化。其他需要互斥访问单例对象的方法,也可以通过相同的锁机制进行同步。

public class Singleton {

    private static Object classLock = Singleton.class;
    private static Singleton singleton;
    private Singleton() {}

    public static Singleton getSingleton() {
        synchronized (classLock){
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

getSingleton()方法保证:当一个线程开始初始化单例的实例时,如果另一个线程也开始相同的操作,则第二个线程就会等待,直到获得classLock对象的锁。当他获得锁后,就会发现该实例的对象不再为null(因为类仅可能有一个实例,所以可以使用单个静态锁。

 

识别单例

这种独一无二的对象并不罕见。事实上,应用程序中的很多对象都承担了唯一的职责,既然如此,为何要创建拥有相同职责的两个对象?同样的,几乎每个类都应有独一无二的角色,又何必为相同的类重复开发两次?然而,允许类只能拥有一个实例的单例类却极为罕见。事实上,一个对象或一个类时唯一的,并不意味着就是单例模式。

单例模式或许是最负盛名的一个模式了,但它很容易被误用,因此不要轻易使用。不要让单例模式成为创建全局变量的一种花俏方法。单例模式常常引入一些耦合。应该减少运用单例模式的数量;最好的方式是,类只需知道与它协作的对象,却不必了解创建它所需要的限制。请注意:倘若需要为了测试提供子类或不同的版本,由于单例只能拥有一个实例,因此它可能不是最佳选择。

 

小结

单例模式保证了类仅有一个实例,并为其提供了一个全局访问点。通过延迟初始化(仅在第一次使用它时才初始化),一个单例对象是达到此目的的通用做法。在多线程环境下,必须小心管理线程间的协作,因为他们访问单例对象方法与数据的时间,可能只有毫厘之差。

对象具有唯一性,并不意味着使用了单例模式。单例模式通过隐藏构造函数,提供对象创建的唯一入口点,从而将类的职责集中在类的单个实例中。

 

本文来自-《Java设计模式(第二版)》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值