重新理解单例模式
单例模式是设计模式中应用最广的一种设计模式,其核心就是将构造函数私有化,通过静态方法获取一个唯一的实例,当然在这个过程中要保证线程更是安全的。
常用的单例模式有
懒汉模式
public class Vip {
private static Vip mVip=null;
private void Vip() {}
public static synchronized Vip getVip(){
if(mVip==null){
mVip=new Vip();
}
return mVip;
}
}
饿汉模式
public class Boss {
private static final Boss mBoss=new Boss();
private void Boss(){
}
public static Boss getBoss(){
return mBoss;
}
}
其两种模式的区别就是饿汉模式一个是程序在java虚拟机创建就进行初始化,懒汉模式在首次调用才进行初始化。但是,懒汉模式每次调用都要调用synchronized关键字进行线程同步,造成了不必要的内存开销,反映较慢不推荐使用。从而演变出了DLC (double check lock )从字面意思来看就是双次检查锁定,通过2次空判断来避免懒汉模式中synchronized关键字每次都要进行同步问题。
DLC
public class Vip {
private static Vip mVip=null;
private Vip(){}
public static Vip getInstance(){
if(mVip==null){
synchronized (Vip.class) {
if(mVip==null){
mVip =new Vip();
}
}
}
return mVip;
}
}
但是DLC模式有一个问题,上面的代码可以看作在内存进行了如下几部操作
1.分配实例内存
2.调用构造函数,初始化
3.将synchronized对象指向分配的内存空间(这时mVip不为空)
但是,由于java编译器可以乱序执行,1-2-3的顺序当然没问题,但是如果先1-3然后切换到了B线程,由于执行了3 mVip已经不为空却没有初始化,这时候就导致了DLC失效。当然也就解决方法就是加上volatile关键字。变为 private static volatile Vip mVip=null;
。DLC可以满足大多数场景的要求,但是由于volatile关键字是java 5.0才出现的所以,DLC出现了驱动力不足,而且这也并不是一种高效的优化措施。
静态内部类单例模式
public class Singleton {
private void Singleton() {
}
public static Singleton getSingleton() {
return SingletonHolder.mSingleton;
}
private static class SingletonHolder {
private static final Singleton mSingleton = new Singleton();
}
}
第一次加载Singleton并不会初始化mSingleton只有在第一次调用getSingleton才会导致mSingleton被初始化,因此,第一次调用getSingleton会导致虚拟机加载SingletonHolder类,这种方式不仅可以保证线程安全,也可以保证对象唯一性,同时从代码层面也更加简介,是推荐的单例实现模式。
枚举单例
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
// Log.e("test","test")
}
}
因为枚举在java中和普通的类一样不仅可以有字段,还可以有方法,最重要的是枚举默认创建的线程是安全的,且在任何情况下它都是一个单例。但是,上面的几个案例在一种情况下会导致单例重新创建,那就是序列化,在反序列化的时候会导致对象重新创建的情况。如果想杜绝单例模式在反序列化中重新创建的问题就要实现 readResole方法。
private Object readResolve() throws ObjectStreamException{
return mSingleton;
}
容器单例模式
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();
private SingletonManager() {
}
public static void registerService(String key, Object obj) {
if (!objMap.containsKey(key)) {
objMap.put(key, obj);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
利用Map的特性可以将多个单例放到一个容器中管理,根据key得到对应的对象
单例在安卓源码中的使用
嗨呀!我也不知道后面怎么写。很乱等我整理出来单独开一章。