Android架构进阶(四)—— 单例模式

单例模式

文章目录

  • 单例模式
    • 1、单例模式实现方式
    • 2、饿汉式【使用静态常量】
    • 3、饿汉式【使用静态代码块】
    • 4、懒汉式【效率低不推荐使用】
    • 5、懒汉式【Double-Check,推荐使用】
    • 6、静态内部类【推荐使用】
    • 单例模式的优缺点
    • 适用场合

单例模式定义:在同一进程内,单例对象的类只允许存在一个实例。

  • 单例模式通常要求仅存在一个实例,那就是说无论什么情况下都要求,我们只能创建一次实例。
  • 当我们未创建实例时,有多个线程同时调用实例创建方法,有时候会导致两个实例同时被创造出来。
  • 所以为了解决线程安全问题,我们通常为指示类是否实例化的变量提供一个互斥锁

1、单例模式实现方式

单例模式需要类包含提供外部使用的同一个实例对象,和一个获取该对象的方法

通常获得实例的方法都是静态方法且命名为getInstance()方法。

  • 将该类的构造方法定义为私有方法,这样其他类就无法通过new关键字去构造这个对象,只能通过getInstance()方法获取该对象
  • 在该类内提供的getInstance()静态方法中,我们判断如果累持有的提供给外部的实例对象为空时我们就创建一个实例并赋予该实例对象引用,如过不为空时,我们就返回这个实例对象。

2、饿汉式【使用静态常量】

public class A{
	private final static A instance = new A();
	private A(){}
	public static A getInstance(){
		return instance;
	}
}
优点 实现简单粗暴,在类装载时就实例化好了内部实例对象,可以避免线程同步问题
缺点 在类装载时就实例化了内部实例,如果从未使用这个实例,对象依旧存在,浪费内存资源

3、饿汉式【使用静态代码块】

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

除了实现方式不同,与上一种方法基本类似。

优点 实现简单粗暴,在类装载时就实例化好了内部实例对象,可以避免线程同步问题
缺点 在类装载时就实例化了内部实例,如果从未使用这个实例,对象依旧存在,浪费内存资源

4、懒汉式【效率低不推荐使用】

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

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

这样我们就实现了懒加载,但是只能在单线程模式下工作,如果多线程同时调用getInstace()方法就会容易产生多个实例,所以切记最好不用这种方式。

那么我们如何解决多线程下调用创建方法会创建多个实例这种问题呢,这里我们引入synchronized关键字,将getInstance()方法锁住只允许多个线程按顺序排队调用getInstance(),这样就不会出现同时判断instance != null 这种情况,避免了创建多个实例

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

	public static synchronized A getInstance(){
		if(instance != null){
			instance = new A();
		}
		return instance;
	}
}
优点 实现了懒加载,即不会再类装载的时候就创建内部单例实例,只有当第一次调用getInstance()方法时才会创建实例,避免了占用额外内存资源
缺点 效率过低。每个线程在想获得这个类的实例的时候,都必须进行同步,然而我们其实只是想要在实例化时限制同步,后面获取实例时直接return当前实例就OK

5、懒汉式【Double-Check,推荐使用】

由于上种方式效率过低,所以我们进行改进

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

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

这样虽然效率提升了,但是还是会出现最开始时的多次创建实例的情况,那么,我们就通过Double-Check进行进一步改进

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

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

经过这样改进后,除了代码多了一点意外,基本上属于比较完美的实现方式了,线程安全,延迟加载,效率较高。

6、静态内部类【推荐使用】

这里我们利用了JVM的特性,类的静态属性只会在第一次加载类的时候初始化,利用JVM这个特性保证了线程的安全性,因为在类进行初始化时,别的线程是无法进入的。

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

这种方式是java特有的实现方式,利用了JVM的特性,即满足了延迟加载——只有当第一次调用getInstance()方法时,才会装载内部类AInstance,避免了线程不安全,延迟加载,效率高。

单例模式的优缺点

优点 在系统内只存在该类的一个对象,节省了系统资源,可以提高性能
缺点 不常用的实例使用单例模式时会导致该对象一直存在,需要手动释放

适用场合

  • 需要频繁创建和销毁的对象;
  • 创建对象的过程中耗时过多或资源耗费过多,且常用的对象;
  • 工具类的对象通常可以使用这种方法
  • 频繁访问的数据库或文件对象

你可能感兴趣的:(技术贴,Android)