单例模式定义:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
使用场景
确保某一个类有且只有一个对象的场景,避免产生多个对象,消耗过多的资源,或者某一种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。
关键点
1 构造函数不对外开放,一般为Private;
2 通过一个静态方法或者枚举返回单例类对象;
3 确保单例类的对象有且只有一个,尤其是在多线程环境下;
4 确保单例类对象在反序列化时不会重新构建对象;
如何使用?
一般Singleton模式通常有两种形式:
第一种形式(饿汉式):
public class Singleton {
private Singleton(){}
//注意这是private 只供内部调用
private static Singleton instance = new Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance() {
return instance;
}
}
第二种形式(懒汉式):
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次
//使用时生成实例,提高了效率!
if (instance==null)
instance=new Singleton();
return instance; }
}
使用Singleton.getInstance()可以访问单态类。
上面第二中形式是懒汉式,也就是说第一次调用时初始Singleton,以后就不用再生成了。
注意到懒汉式形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。一般认为第一种形式要更加安全些。
懒汉式优点:只有在使用时才会被实例化,在一定程度上节约了资源;缺点:第一次加载时需要及时进行实例化,反应稍慢,最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销。
-----------------------------------------------------------
多线程时的单例
//解释一下锁lock语句的含义,lock是确保当一个线程位于代码的临界区
//时,另一个线程不进入临界区.如果其他线程试图进入锁定的代码,则它将一直等待
//(即被阻止),直到该对象被释放。
public class Singletons {
private static Singletons instance;
//程序运行时创建一个静态只读的进程辅助对象
private static readonly object syncRoot=new object();
public Singletons(){}
public static Singletons GetInstance(){
//在同一个时刻加了锁的那部分程序只有一个线程可以进入
lock(syncRoot)
{
if(instance==null){
instance=new Singletons();
}
}
return instance;
}
}
---------------------------------------------------------------
双重检查(DCL):
第一种写法:
public class Singleton {
private static Singleton instance;
private static readonly object syncRoot=new object();
private Singleton(){}
public static Singleton GetInstance(){
//先判断实例是否存在,不存在再加锁处理
if(instance==null)
{
lock(syncRoot)
{
if(instance==null)
{
instance=new Singleton();
}
}
}
return instance;
}
}
//为什么lock里面还需要做一次instance判断?在lock机制下,两个
//线程只有一个进入,另一个排队等候,在第一个进入并出来后,第二个才能进入,
//此时如果没有了第二重的instance是否为null的判断,则第一个线程创建了实例后
//而第二个线程进入后还是可以继续再创建新的实例,这就没有达到单例的目的。
第二种写法:
public class Single {
public static Single s=null;
private Single(){
}
public static synchronized Single getInstance(){
if(s==null){
synchronized (Single.class) {
if(s==null){
s=new Single();
}
}
}
return s;
}
}
分析:可以看到getInstance方法对s进行了两次判空:第一层判断是为了避免不必要的同步,第二层判断则是为了在null的情况下创建实例。假如线程执行到了s=new Single()语句,它大致做了3件事情:1)给Single的实例分配内存;2)调用Single()的构造函数,初始化成员字段;3)将s对象指向分配的内存空间(此时s就不是null了).
DCL的优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。缺点:第一次加载时反应稍慢。
lock与synchronize的区别是什么?
解答:参考
Android的应用
在Glide图片加载框架中的应用:
public class RequestManagerRetriever implements Handler.Callback {
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
// Visible for testing. 注:此构造方法只在本包中可见
RequestManagerRetriever() {
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
public static RequestManagerRetriever get() {
return INSTANCE;
}
}
在Android FrameWork层中的应用:
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
** @hide */
public class ResourcesManager {
private static ResourcesManager sResourcesManager;
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
if (sResourcesManager == null) {
sResourcesManager = new ResourcesManager();
}
return sResourcesManager;
}
}
}