源码设计模式读书笔记4-单例

1.单例模式

    整个系统只需要拥有一个全局对象,在Android中,一般的意思是,在一个进程中只存在一个全局对象。确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。主要是为了避免产生多个对象消耗过多资源,或者某个类型的对象只应该有且只有一个。比如,前者,IO或者数据库等资源,就需要考虑使用单例模式。
   

    需要注意一下几点:
    (1)构造函数private;
    (2)通过一个静态方法或者枚举返回单例类对象,需要确保线程安全;
    (3)确保单例类的对象有且只有一个,尤其是在多线程环境下;
    (4)确保单例类对象在反序列化时不会重新构建对象。
    

    1.懒汉模式:

    
public class Singleton {
	private static Singleton instance;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
    优点:只有在使用的时候才被实例化,在一定程度上节约了资源。
    缺点:第一次加载时需要及时进行实例化,反应稍慢;每次调用getInstance()都需要进行同步,造成不必要的开销。不建议使用。

    2.饿汉模式:

public class Singleton {
	private static final Singleton instance = new Singleton();

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

    3.Double CheckLock(DCL)单例模式:

    
public class Singleton {
	private static Singleton instance = null;

	private Singleton() {

	}

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

    优点:既能够在需要时才初始化单例,又能够保证线程安全,并且在单例对象初始化后调用getInstance()不进行同步锁操作。
    缺点:可能会失效。
    分析: instance = new Singleton();
    并不是原子操作,它大致由3条指令构成:
    (1)给Singleton的实例分配内存;
    (2)调用Singleton()方法,初始化成员字段;
    (3)将instance对象指向分配的内存空间(此时instance != null了)。
    java 1.5之前上面2,3是乱序的,如果在3执行完毕,2未执行前切换到了另外一个线程,那么线程获取的instance就会出现未初始化的问题,就会导致DCL失效。
    java 1.5之后具体化了volatile关键字,所以在java1.5之后需要将instance定义改成
private volatile static Singleton instance = null;
    就可以保证每次都是从主内存中读取,就可以使用DCL的写法完成单例模式。它是使用最多的单例实现方式,不过不赞成使用。

    4.静态内部类单例模式:

    
public class Singleton {
	private Singleton() {
	}

	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}

	private static class SingletonHolder {
		private static final Singleton instance = new Singleton();
	}
}
    推荐使用。

    5.枚举单例:

    上边所有的方法都有一个问题就是在反序列化的时候回重新创建出一个对象,如果需要解决这个问题,需要实现readResolve()方法。而枚举与普通类一样,不仅能够有字段们还能够有自己的方法,最重要的时候默认枚举实例的创建是线程安全的,并且在任何情况下都是一个单例,反序列化也不会创建新的对象。
使用readResolve方法的例子如下:
public class Singleton implements Serializable {
	private Singleton() {
	}

	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}

	private static class SingletonHolder {
		private static final Singleton instance = new Singleton();
	}

	private Object readResolve() throws ObjectStreamException {
		return SingletonHolder.instance;
	}
}

    使用枚举可以解决以上问题。

public enum SingletonEnum{
	INSTANCE;
	public void doSomeThing(){
		System.out.println("=========test");
	}
}
     优点:简单,线程安全,反序列化不会生成新对象。

   使用容器实现单例模式:

    
public class SingletonManager {
	private static Map<String, Object> objMap = new HashMap<String, Object>();

	public static void registerService(String key, Object instance) {
		if(!objMap.containsKey(key)){
			objMap.put(key, instance);
		}
	}
	
	public static Object getService(String key){
		return objMap.get(key);
	}
}
    将多种单例类型注入到一个统一的管理类中,通过key获取单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户使用成本,对用户透明,降低了耦合性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值