什么是单例模式?

单例模式(Singleton) 是最常见也最简单的设计模式,它的目的就是在全局只生成一个类的实例。

什么场合用单例模式

应用中经常有多任务进行信息共享的需求,比如火车票售卖示例中,多个售票窗口其实共享一个票务池。如果一个票务池用一个类,那么这个类就只能用一个实例,否则多任务进行时会引起资源同步的问题。

另外,频繁创建和销毁的对象也可以用一个固定的实例,这样的好处是节省内存重复创建和销毁的开销,提高程序的稳定性。

面向对象的编程很容易实现单例模型,比如 Java、C++ 等等,本文以 Java 代码讲解。

单例模型的核心思想就是:私有化构造方法,只开放静态的获取方法。

单例模式的实现手段(Java)

饿汉式

// 饿汉式单例
public class SingletonHungry {// 主动创建静态的私有实例private static SingletonHungry singleton = new SingletonHungry();// 私有的构造方法private SingletonHungry(){}//静态的公开的实例获取方法public static SingletonHungry getInstance(){return singleton;}
}

饿汉式的例子一看就懂,不管三七二十一先创建了对象再说,不同的进程通过 getInstance 获取的都是同一个对象,所以是线程安全的。

但也有个不好的地方就是,如果某个类创建过程会消耗很多资源,但程序运行中没有调用过 getInstance 方法,那么就存在资源浪费的情况,如果一个系统存在非常多此类情况那么这个系统可能存在性能上的问题。

所以,我们需要一种延迟加载的功能。

懒汉式

public class SingletonLazy {  private static SingletonLazy instance;  private SingletonLazy (){}  public static SingletonLazy getInstance() {  if (instance == null) {  instance = new SingletonLazy();  }  return instance;  }
}

懒汉式在饿汉的基础上做了改进,先判断 instance 是否为空,如果为空则创建示例,否则直接返回。

懒汉式做到了延迟加载,非常适合单线程。

但多线程下面会存在问题,如果多个线程同时调用 getInstance 方法,可能存在同时判断 instance 变量是否为空的情况,上面的代码中很容易导致重复创建多个实例,这违背了单例模式的目的。

一般而言,我们会进行一些同步手段。

双检锁(DCL,double-checked locking)

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

DCL 进行两次 instance 的判断,并且利用了关键字 synchronized 。

试想当多个线程同时调用 getInstance 时,可能会存在同时判断 instance 为空的情况。

但因为 synchronized 关键字的存在,同一个时刻只能一个线程进入代码同步区域,其它线程会被阻塞。

被阻塞的线程重新获得进入代码临界区时,再次判断 instance 就好了。

静态内部类

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

这种方法也可以做到延迟加载,但是它又不同于饿汉式。

因为只有调用 getInstance 时,SingletonHolder 才会进行初始化。

之前做 Android 开发时,涉及到图片缓存加载的时候经常会看到一些开源组件有各类 ImageHolder 的代码,原理正是如此。

Android 源码中的单例模型

以 Android 系统版本为 9.0.0 代码为例,它的 framework 包中有一个 Singleton.java 文件。

package android.util;/*** Singleton helper class for lazily initialization.** Modeled after frameworks/base/include/utils/Singleton.h** @hide*/
public abstract class Singleton<T> {private T mInstance;protected abstract T create();public final T get() {synchronized (this) {if (mInstance == null) {mInstance = create();}return mInstance;}}
}

它是一个抽象类,并且支持泛型,从而支持模板化的形式创建任意类型的对象的单例。

源码很简单,通过 DCL 实现了懒加载。

我们再看具体点。

xref: /frameworks/base/core/java/android/app/ActivityManager.java


/*** @hide*/
public static IActivityManager getService() {return IActivityManagerSingleton.get();
}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}};

ActivityManager 是 Android 最核心的系统服务之一,它的代码有 4000 多行,上面是部分代码。它通过一个隐藏的 getService 调用而创建。

创建过程就是通过上面的 Singleton 实现。

从这个角度看,Android Framework 代码其实也不是很难是嘛,相信自己,你也可以写出很多类似代码出来。

设计模式(一):Android 源码中的单例模式相关推荐

  1. android单例模式代码,设计模式(一):Android 源码中的单例模式

    设计模式(一):Android 源码中的单例模式 2020-08-17 22:51 阅读数 57 <>什么是单例模式? 单例模式(Singleton) 是最常见也最简单的设计模式,它的目的 ...

  2. android源码使用方法,android源码中使用到的设计模式(创建型)

    1.单例模式 1.1定义 确保某个类只有一个实例,而且自行实例化并向整个系统提供者个实例. 1.2单例的形式 饿汉模式:第一次就加载,用空间换时间. public class SingleTon { ...

  3. android 源码中的单例,Android源码中的一种单例实现

    单例模式的实现方式有懒汉,饿汉,双重校验锁,枚举,内部类等等,写法就不全部列举了.Android源码中有一个单例辅助类/frameworks/base/core/java/android/util/S ...

  4. 13-MyBatis 使用了哪些设计模式?在源码中是如何体现的?

    MyBatis 的前身是 IBatis,IBatis 是由 Internet 和 Abatis 组合而成,其目的是想当做互联网的篱笆墙,围绕着数据库提供持久化服务的一个框架,2010 年正式改名为 M ...

  5. android 静态工厂方法,Android 源码中的静态工厂方法

    我们知道工厂模式有三兄弟,通常我们说的工厂模式指的是工厂方法模式,它的应用频率最高.本篇博客分享的简单工厂模式是工厂方法模式的"小弟",确切的来讲它不属于设计模式,而是一种方法.此 ...

  6. 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )

    文章目录 一.DexFile 构造函数 二.DexFile.openInMemoryDexFile 函数 三.Android 源码中查找 native 函数 一.DexFile 构造函数 上一篇博客 ...

  7. Android源码中的FLAG为何使用16进制

    1.在阅读源码的时候经常发现有一些标志属性使用一些位操作来判断是否具有该标志,增加标志或者去除标志. 比如View.java中的 /*** This view does not want keystr ...

  8. android底层截图,Android源码中屏幕截图的实现

    Android手机一般都自带有手机屏幕截图的功能:在手机任何界面(当然手机要是开机点亮状态),通过按组合键,屏幕闪一下,然后咔嚓一声,截图的照片会保存到当前手机的图库中,真是一个不错的功能! 以我手头 ...

  9. android 指令模式,Android 源码中的命令模式

    原标题:Android 源码中的命令模式 (点击上方公众号,可快速关注) 来源:伯乐在线专栏作者 - PleaseCallMeCoder 链接:http://android.jobbole.com/8 ...

最新文章

  1. “禁止大数据杀熟”拟入法!个性化推荐功能也应提供拒绝选项
  2. 生产者消费者模型的学习
  3. 机器学习——人工神经网络之后向传播算法(BP算法)
  4. Java提高篇 —— 抽象类与接口
  5. windows7如何实现屏幕不休眠
  6. 为什么说“医可救众生?佛可救众生!”
  7. 一个JS多个数组取交集算法
  8. IE无法打开新链接的问题
  9. hmtl--textarea的滚动条(转)
  10. Oracle必读好书推荐
  11. 阿里云网站备案基础知识-什么是网站备案
  12. mino文件服务器,mimo技术有什么用_mino技术原理解析
  13. Mybatis报错: Could not find resource mapper
  14. 惠普服务器停电后进不了系统,惠普电脑出现了startup menu 然后按f10进不去bios。进入的是Windows启动项...
  15. wifi连接速率 linux,Deepin linux 操作系统提高 WiFi 速度
  16. 《计算机组成与设计:硬件、软件接口》阅读笔记
  17. Android 自定义View颜色选择器( 条形、水平),使用HSV颜色模型实现取色器并反向定位颜色所在位置
  18. 关于网络游戏防沉迷系统
  19. java jit aot_JIT和AOT编译详解
  20. 电力工程设计一次回路—(330~500KV超高压配电装置)一个半断路器接线方式

热门文章

  1. 构建银行人工智能用户画像和自动营销体系
  2. WAMP Server 安装教程
  3. PVE(Proxmox VE) 显卡直通
  4. java main 运行_使用maven运行Java Main的三种方法解析
  5. Linux rm命令 回收机制
  6. MATLAB代码:考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化
  7. jmeter阶梯式加压测试小笔记
  8. 按头安利 好看又实用的游戏场景3d模型素材看这里
  9. imx53 uboot tftp nfs启动, linux tftp,复制gdb, linux host 创建sd卡启动,ddr stress tester
  10. 利用mysql上传木马_通过Mysql语句生成后门木马的方法_MySQL