1. 定义
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
2. 特点
单例模式有 3 个特点:
a.单例类只有一个实例对象;
b.该单例对象必须由单例类自行创建;
c.单例类对外提供一个访问该单例的全局访问点;
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
3. 单例模式的实现
3.1 饿汉式
代码实现:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
}
优缺点:
优点:类加载的时候创建一次实例,线程安全的。
缺点:类装载的时候就会创建类实例,即使单例没有用到也会创建,浪费内存。
3.2 懒汉式(非线程安全)
代码实现:
public class Singleton {
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点:
优点:需要时才去创建,跟饿汉式相比,如果没有用到不会去创建,不会浪费内存。
缺点:此种实现方式没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例
3.3 Double Check Lock(DCL方式)
代码实现:
public class SingleTonLazy
{
private static volatile SingleTonLazy instance;
private SingleTonLazy(){}
public static SingleTonLazy getInstance()
{
if(instance==null) //第一次检查
{
synchronized (SingleTonLazy.class)
{
if(instance==null) //第二次检查
{
instance=new SingleTonLazy();
}
}
}
return instance;
}
}
优缺点:
优点:资源利用率高,不执行getInstance就不会被实例,多线程下效率高。
缺点:第一次加载时反应不快,由于Java 内存模型一些原因偶尔会失败,在高并发环境下也有一定的缺陷,发生概率很小。
3.4 静态内部类
代码实现:
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法保证了线程安全问题。由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类SingletonHolder,在该内部类中定义了一个static类型的变量INSTANCE ,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;
4. 总结
3.1的方式是线程不安全的,其他方式是线程安全的。一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)可以使用 DCL 方式或者静态内部类,当前 Android 比较流行的加载图片框架 Glide 用的是 DCL 的方式。代码如下所示:
/**
* Get the singleton.
*
* @return the singleton
*/
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}