设计模式-单例模式

150 阅读4分钟

单例模式

引言

俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Singleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事) 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

正文

​ 单例模式,主要是用来保证实例化的唯一性,使开发者可以控制其生命周期。

Singleton
-instance:Singleton
-Singleton(),+getInstance()

常见的单例模式的写法可以分为5种,分别是饿汉式、懒汉式、双重校验锁、枚举和静态内部类。

饿汉式

饿汉式是指类的实例在类被加载时就进行创建,也正是由于是在类加载时就创建,因而其可以保证线程安全。

public class Singleton {

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

}

但是这种写法存在两个缺点:

  1. 如果在这个类的构造方法中进行其他的处理,比如初始化一些控件等,那么这个类在加载时就会比较浪费时间,同时由于这个类的实例一直存在,也可能引起性能问题。
  2. 由于这个类在程序启动时就一直存在,如果不进行调用,也会引起性能问题。

懒汉式

由于饿汉式在程序启动时就进行创建,为了避免饿汉式的第2个缺点,懒汉式选择在程序需要实例化它的时候才进行实例化。

public class Singleton {

	private static Singleton instance;

	private Singleton() {
	}

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

}

懒汉式的缺点就是这种写法只适用于单线程,在多线程下就会出现问题。比如说线程A在执行instance == null时,此时线程B也执行这段代码,那么就会创建两个实例,也就违背了单例原则了。解决的办法就是采取对**getInstance()**方法加锁,但这样就会使这段代码的性能下降。

双重校验锁

为了解决上述性能下降的问题,可以对**getInstance()**方法内部实行加锁后双重判断。

public class Singleton {

	private static Singleton instance;

	private Singleton() {
	}

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

}

双重校验锁方法保证在多线程的程序下,永远只会创建一个实例,那么为什么双重校验锁方法性能比懒汉式的好呢?是因为他的加锁代码只是在创建实例之前进行同步,如果实例已经创建好了,再次访问该方法时,不会执行被加锁的代码。

静态内部类

public class Singleton {

	private static Singleton instance;

	private Singleton() {
	}

	public static Singleton getInstance() {
	
		return SingletonInstanceHolder.instance;
	}
	
	private static class SingletonInstanceHolder{
		private static Singleton instance = new Singleton();
	}

}

在上述代码中,Singleton类被加载后,instance则不一定会被初始化。因为SingletonInstanceHolder没有被主动使用,只有显式的调用getInstance()时,才会显式加载SingletonInstanceHolder类,从而实例化instance,从而实现了延迟加载instance的效果。

枚举

public enum Singleton {

	INSTANCE;
	public void dosth(){}

}

枚举方法可以通过Singleton.INSTANCE来访问,既方便又安全。还可以防止反序列化创建新的对象,但是失去了类的特性,没有延迟加载。

Android开发中的单例模式

在Android开发中,单例模式是非常常见的,比如说一个APP中有且只有一个application,那么我们在写自己的application时,可以采用如下的方式。

public class MyApplication extends Application {
    private static MyApplication instance = null;
 
    public static MyApplication getApplication() {
        return instance;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
 
        instance = this;
    }
}

这样写的原因是,app在启动过程中,一定会执行Application类的onCreate()方法,那么也就不需要像上面那么麻烦加锁。这样当然是有Application的特殊性,其他的一般情况下,建议采用静态内部类。

总结

一般情况下,可以选择采用饿汉式,因为简单,而且是jvm下安全的。但在开发过程中,静态内部类是大多数开发者采用的方式。