Android开发中单例模式四种实现方式详解

Posted 码农 小生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发中单例模式四种实现方式详解相关的知识,希望对你有一定的参考价值。

为何用单例模式以及应用场景

  1. 整个程序的运行中只允许有一个类的实例;
  2. 需要频繁实例化然后销毁的对象。
  3. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  4. 方便资源相互通信的环境,
  5. 封装一些常用的工具类,保证整个应用常用的数据统一
  6. 保存一些共享数据在内存中,其他类随时可以读取。
  7. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置
  8. 控制资源的情况下,方便资源之间的互相通信。如线程池等。

为什么用单例大家都清楚了 废话不说,直接上代码,盘它就完了!!!!

一.饿汉式

public class SingletionStarving {

    private static final SingletionStarving mInstance = new SingletionStarving();

    private SingletionStarving() {

    }

    public static SingletionStarving getInstance() {
        return mInstance;
    }
}
  • 构造函数用private修饰,外部无法访问

  • 声明静态对象时就初始化

  • static关键字修饰,静态变量,存储在内存中,只有一份数据。

  • final关键字,只初始化一次,所以mInstance实例只有一个。

二.懒汉式

public class SingletionSlacker {

    private static SingletionSlacker mInstance;

    private  SingletionSlacker() {}

    public static synchronized SingletionSlacker getInstance() {
        if (mInstance == null) {
            mInstance = new SingletionSlacker();
        }
        return mInstance;
    }
}
  • 构造函数用private修饰,外部无法访问
  • 使用的时候即调用getInstance的时候才初始化
  • static关键字修饰,静态变量,存储在内存中,只有一份数据。
  • synchronized线程安全,多线程情况下单例的唯一性
  • 缺点:没次调用getInstance都会同步一次,浪费资源

三.双重检查加锁方式

public class Singletion {

    private static Singletion mInstance;

    private Singletion() {}

    public static Singletion getmInstance() {
        if (mInstance == null) {
            synchronized (Singletion.class) {                if (mInstance == null) {
                    mInstance = new Singletion ();                }
            }
        }
        return mInstance;
    }
}
  • 构造函数用private修饰,外部无法访问
  • 使用的时候即调用getInstance的时候才初始化
  • static关键字修饰,静态变量,存储在内存中,只有一份数据
  • synchronized线程安全,多线程情况下单例的唯一性
  • 两次判断空,避免多次同步(synchronized)

缺点

private static Singletion mInstance;
private Singletion() {}
public static Singletion getmInstance() {}

由于jvm特性,允许乱序执行,上面三句代码顺序不定,那么就可能出现失效的问题。

步骤一、倘若A线程执行getmInstance(),还没执行构造方法Singletion()
步骤二、此时B线程调用getmInstance()。因为A已经执行getmInstance(),所以mInstance不为空就直接获取。
步骤三、由于B直接获取,而真实情况是A线程构造方法还未执行,所以mInstance就为空了。

虽然此情况发生概率较小,但也是一种情况。为了解决这种情况,java1.6开始加入volatile关键字

private volatile static Singletion mInstance;

这样就避免了方式失效的情况。虽然会volatile消耗一些性能,所以最佳写法

public class Singletion {

    private volatile static Singletion mInstance;
    private Singletion () {}
    public static Singletion getmInstance() {        if (mInstance == null) {
            synchronized (Singletion.class) {                if (mInstance == null) {
                    mInstance = new Singletion();                }
            }
        }
        return mInstance;
    }
}

虽然volatile让方式完美,但是没有volatile关键字的写法基本能满足绝大部分情况。除非你要运行在高并发,或者java1.6之前的代码中。

四.静态内部类方式

public class SingletionInternalClass {

    private SingletionInternalClass() {}

    public static SingletionInternalClass getInstance() {
        return SingletionInternalClassHolder.instance;
    }

    private static class SingletionInternalClassHolder {
        private static final SingletionInternalClass instance = new SingletionInternalClass();
    }
}
  • 构造函数用private修饰,外部无法访问

  • 使用的时候即调用getInstance的时候才初始化

  • 调用getInstance才回去加载SingletionInternalClassHolder类,确保了线程安全,保证了单例的唯一性

总结

单例模式不管用那种方式实现,核心思想都相同
1、构造函数私有化,通过一次静态方法获取一个唯一实例
2、线程安全

最后推荐使用文中双重锁方式和静态内部类的方式来创建单例模式。

以上是关于Android开发中单例模式四种实现方式详解的主要内容,如果未能解决你的问题,请参考以下文章

NopCommerce源码架构学习-二单例模式实现代码分析

Objective-C中单例模式的实现-备

Cocos Creator游戏开发中单例的实现

Android框架设计模式——Singleton Method

Android中单例模式和静态方法在效率上哪个好

Python设计模式中单例模式的实现及在Tornado中的应用