单例模式
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
为什么要使用单例模式:某个类的对象在整个项目是唯一的,它不能也没必要被实例化多次,比如窗口管理器、皮肤加载器等等。这就催生出了如下的现实需求:如何确保某个类只有一个实例。即要使用单例模式。
单例模式有以下特点:
- 单例类只可有一个实例
- 单例类必须自己创建自己这唯一的实例,即构造方法私有,外部类不能new来实例化它
- 单例类必须给所有其他对象提供这一实例的方法,并且开放
单例模式结构图
注意:Singleton() 构造方法是私有,GetInstance()是一个静态的方法,主要负责创建自己的唯一实例。
单例模式创建有两种形式: - 饿汉模式
class Singleton
{
private static final Singleton s = new Singleton(); //final s终生指向这个new Single()
private Singleton(){} //私有,这就堵死外界利用new创建此类实例的可能
public static Singleton GetInstance()
{
return s;
}
}
- 饿汉模式弊端:每个对象在没有使用之前就已经初始化了。这就可能带来潜在的性能问题:如果这个对象很大呢?没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。因此引出下面另外一个模式
- 懒汉式 (延迟加载)
改进版:加上锁class Singleton { private static Singleton s = null; private Singleton(){} public static Singleton GetInstance() { if(s==null) //--->A; //--->B; //---->C; 这里应该会出现同步问题!会创建多个实例 s = new Singleton(); } } return s; } }
这时,我们可以利用双重锁定class Singleton { private static Singleton s = null; private Singleton(){} public static Singleton GetInstance() { synchronized(Singleton.class) //使得最先进入的那个线程创建,以后的在进入是就不能实例化 每次进来都要判断一次是不是效率很低? { if(s==null) //--->A; //--->B; //---->C; s = new Singleton(); } return s; } }
class Singleton { private static Singleton s = null; private Singleton(){} public static Singleton GetInstance() { if(s==null) <执行到第三个就不用上锁了> { // A 锁定后,刚创建实例时,此时B线程已经运行到这儿,A给资源B后 synchronized(Singleton.class) { if(s==null) //B又判断一次,此时已经实例化,B不会new了。 s = new Singleton(); } } return s; } }
看似完美,但是还会有个缺陷的,例如A线程在执行到s = new Singleton();实例化时后,因为初始化是比较耗费时间的,但是这个对象的地址其实已经存在了。此时刚好B线程执行到第一个if(s==null) ; 判断不为null时就直接返回S,此时会得到一个还没有完全初始化的对象! - 但往往面试时候提到饿汉式的双重锁定就足够了,不用深入探究吧!
注意:
单例模式类似于Math实用类的采用私有化避免实例化,但是实用类只提供一些静态方法或者静态属性让我们使用,而单例类是有状态的,另外,实用类不能用于继承多态,而单例虽然唯一,却是可以继承的。实用类只不过是一些方法属性的集合,而单例却是唯一的对象实例。