设计模式学习4——单例模式

本文详细介绍了单例模式的概念、特点及其实现方式,包括饿汉式、懒汉式及双重检查锁定等,并对比了不同实现方式的优劣。此外,还讨论了单例模式的最佳实践及其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参看:http://blog.youkuaiyun.com/lovelion/article/details/7420889

一、模式描述

        单例模式(Singletion Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

结构图:

角色:

        Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例。


各种单例模式的实现:

       单例模式必须保证实例在系统中的唯一性,防止创建多个对象,是单例模式的一个重要目标。为了防止创建多个对象,出现了不同的实现方法。

<1>、饿汉式单例

      饿汉式单例在类加载时就创建单例对象,因此后续的使用就不可能创建多个对象。代码如下:

class EagerSingleton {   
    private static final EagerSingleton instance = new EagerSingleton();   
    private EagerSingleton() { }   
  
    public static EagerSingleton getInstance() {  
        return instance;   
    }     
}  

<2>懒汉式单例与双重检查锁定(Double-Check Locking)

     懒汉式单例在第一次调用getInstance()方法时实例化,同时对getInstance()方法进行同步控制,从而保证对象的唯一性。

例如:直接用synchronized关键字修饰getinstance()方法,代码如下:

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


    上述代码的问题是每次调用getInstance()方法时都要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。

     使用双重检查锁定(Double-Check Locking)来改进懒汉式单例,如下:

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;   
    }  
} 

注意:静态成员变量instance之前增加修饰符volatile。
<3>、饿汉式与懒汉式的比较

       饿汉式单例类在类被加载时就将自己实例化,它的优点是无须考虑多线程访问问题,系统加载时需要创建对象,加载时间可能会比较长。

       懒汉式单例类在第一次使用是创建,实现了延迟加载。需要多线程机制控制同步,影响系统运行性能。

<4>IoDH单例实现——最好的Java语言单例模式

      通过 Initialization Demand Holder(IoDH)技术,实现延迟加载并且减少同步控制的单例类,(在单例类中增加一个静态内部类,在该内部类中创建单例对象,再将单例对象通过getInstance()方法返回给外部使用)代码如下:

//Initialization on Demand Holder  
class Singleton {  
    private Singleton() {  
    }  
      
    private static class HolderClass {  
            private final static Singleton instance = new Singleton();  
    }  
      
    public static Singleton getInstance() {  
        return HolderClass.instance;  
    }  
      
    public static void main(String args[]) {  
        Singleton s1, s2;   
            s1 = Singleton.getInstance();  
        s2 = Singleton.getInstance();  
        System.out.println(s1==s2);  
    }  
} 
      通过使用IoDH,可以实现延迟加载(因为Singleton类加载时不会加载静态内部类,而是当第一次调用getInstance()时将加载内部类HolderClass,此时会实例化Singleton),又可以保证线程安全(因为仅在静态内部类加载时初始化一次,由Java虚拟机来保证其线程安全性),不影响系统性能,是最好的Java语言单例模式实现方式。


二、模式优缺点

        主要优点:

        <1>、单例模式提供了对唯一实例的受控访问。

        <2>、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

        <3>、允许可变数目的实例。基于单例模式我们可以扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

        主要缺点:

         <1>、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

         <2>、单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身功能融合在一起。

         <3>、现代面向对象语言提供了自动垃圾回收技术,如果实例化的共享对象长时间不被利用,系统会认为是来及,会自动销毁并回收资源,下次利用时又将重新实例化,导致共享的单例对象状态的丢失。

三、适用场景

        <1>、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。

        <2>、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。


四、案例分析



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值