单例模式解析

定义

由于某种需要,要保证一个类在程序的生命周期当中只有一个实例,并且提供该实例的全局访问方法。

结构

一般包含三个要素:

1、私有的静态的实例对象 private static instance

2、私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例) private Singleton(){}

3、公有的、静态的、访问该实例对象的方法 public static Singleton getInstance(){}

应用场景

单例模式在程序设计中非常的常见,一般来说,某些类,我们希望在程序运行期间有且只有一个实例,原因可能是该类的创建需要消耗系统过多的资源、花费很多的时间,或者业务上客观就要求了只能有一个实例。一个场景就是:我们的应用程序有一些配置文件,我们希望只在系统启动的时候读取这些配置文件,并将这些配置保存在内存中,以后在程序中使用这些配置文件信息的时候不必再重新读取。

分类

单例模式就实例的创建时机来划分可分为:懒汉式饿汉式两种。

懒汉式

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

顾名思义懒汉式就是应用刚启动的时候,并不创建实例,当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。可以节省系统资源,体现了延迟加载的思想。是以时间换空间。

懒汉式的缺点:由于系统刚启动时且未被外部调用时,实例没有创建;如果一时间有多个线程同时调用LazySingleton.getLazyInstance()方法很有可能会产生多个实例。也就是说下面的懒汉式在多线程下,是不能保持单例实例的唯一性的,要想保证多线程下的单例实例的唯一性得用同步,同步会导致多线程下由于争夺锁资源,运行效率不高。

饿汉式

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  

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

顾名思义懒汉式就是应用刚启动的时候,不管外部有没有调用该类的实例方法,该类的实例就已经创建好了。写法简单,在多线程下也能保证单例实例的唯一性,不用同步,运行效率高。以空间换时间。

饥汉式的缺点:在外部没有使用到该类的时候,该类的实例就创建了,若该类实例的创建比较消耗系统资源,并且外部一直没有调用该实例,那么这部分的系统资源的消耗是没有意义的。

改进版的懒汉模式

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

为了解决懒汉模式在多线程情况的问题,我们使用synchronized关键词同步代码,可以避免了同步问题。这种改良方式的确会解决多线程的问题,但同时synchronized会导致每次方法调用时锁住Singleton实例一次,性能消耗太大。而且instance一旦初始化后,同步锁更加拖累效率。

双重锁定的懒汉模式

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
    return singleton;  
    }  
}  

这种模式避免了域在被初始化之火访问这个域的锁定开销。这种模式的基本思想是:
第一次检查时看看有没有锁定,看一看这个域是否已被初始化了;第二次检查时有锁定,只有当第二次检查时表明这个域没有被初始化,才开始进行初始化。

这种模式既规避了延迟加载也避免了重复加锁带来的额外性能消耗。

静态内部类

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
} 

当getInstance方法第一次调用时,第一次读取SingletonHolder.INSTANCE,导致SingletonHolder类得到初始化,这种方式并没有使用任何线程同步的机制,就保证了一次访问域代码。延迟初始化没有增加访问成本,这个方法值得推荐

总结

5种方式,相对比较推荐第4种和第5种,做到了延迟加载,也没有引入额外的问题和性能开销。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值