单例(Singleton)模式

定义

单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。

GoF对单例模式的定义是:保证一个类,只有一个实例存在,同时提供该实例加以访问的全局访问方法。

使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多资源,或者某种类型的对象只应该有且只有一个。

uml图

角色介绍:

  1. Client--高层客户端
  2. Singleton--单例类

实现单例主要有以下几个关键点

  1. 构造函数不对外开放,一般为private
  2. 通过一个静态方法或者枚举返回单例类对象
  3. 确保单例类的对象有且只有一个,尤其是在多线程环境下
  4. 确保单例类对象在反序列时不会重新构建对象。

单例模式的各种写法

饿汉式

代码:

class HungrySingleton{
    private static HungrySingleton singleton=new HungrySingleton();
    //构造函数私有
    private HungrySingleton(){}
    //静态方法获取实例
    public static HungrySingleton getInstance(){
        return singleton;
    }
}

优点:饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

缺点:资源利用效率不高,可能getInstance永远不会执行到,但是执行了该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化了

懒汉式

代码:

class LazySingleton{
    private static LazySingleton singleton;
    //构造方法私有
    private LazySingleton(){
    }
    //synchrocized 保证了同步
    public static synchronized LazySingleton getInstance(){
        if(singleton==null){
            singleton=new LazySingleton();
        }
        return singleton;
    }
}

优点:单例只有在使用时才会被实例化,在一定程度上节约了资源。

缺点:synchrocized是getInstance方法在多线程情况下保证单例对象唯一性手段,细想一下,即使singleton在第一次的时候已经被初始化了,每次调用getInstance方法都会进行同步,这样就会消耗不必要的资源,这也是懒汉式存在的最大问题。所以一般情况下不建议使用这种情况。

双重检查锁定(Double Check Lock(DCL))

代码:

public class Singleton {
    //使用volatile关键字
    private static volatile Singleton sInstance=null;
    private Singleton() {

    }

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

在getInstance方法中对instance进行了两次判空:第一层判断是为了避免不必要的同步,第二层的判断则是为了在null的情况下创建实例。




下面详细说明这个问题:

假如线程 A 执行到了sInstance = new Singleton();语句,这看起来是一句代码,但在句代码大致做了三件事:

  1. 给 Singleton 分配内存,
  2. 调用 Singleton 的构造函数,初始化成员字段。
  3. 将 sInstance 对象指向分配的内存空间(这个时候 sInstance 就不是 null 了)

由于java编译器是允许处理器乱序执行,上面的第二,第三的顺序是无法保证的,就是说上面的执行顺序可能是 1,2,3 也可能是1,3,2。这样就有问题了,如果线程A中执行的顺序是1,3,2,那么在3执行完毕,2 未执行之前(这个时候 sInstance 就不是 null),被切换到线程B上,由于sInstance不为null,所以线程B直接获取了sInstance,由于这个sInstance没有执行2,所以使用时会出现错误。

上面就是DCL失效问题。

在jdk1.5 以后,SUN注意到这个问题,调整了jvm,具体化了 volidate 关键字,所以在jdk1.5以后使用volidate就可以保证sInstance 对象每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式。

优点:资源利用率高,第一次执行getInstance时才会被实例化,效率快。

缺点:第一次加载时反应有点慢。

静态内部类

代码:

public class Singleton {
    //构造函数私有
  private Singleton() {

    }
    //获取实例
  private static Singleton getInstance() {
        return SingletonHolder.sInstance;
  }

    /**
 * 静态内部类 */
  private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
  }
}

这种方式不仅保证了线程安全,也保证单例对象的唯一性,同时也延迟了单例的实例化,所以推荐使用这种方法来打造单例模式。

枚举

代码:

public enum Singleton {
    INSTANCE
}

枚举写法简单,枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例。

在Android中运用

我们在应用中经常需要关闭页面的操作,如果直接使用finish(),有很多效果达不到,这就需要我们对Activity进行统一管理

Android系统有自己的Activity管理机制,也就是 Activity Stack(栈)。奉行着 先进后出,后进先出的原则。那么我们就通过Stack来进行Activity的管理。

实现包括:添加Activity到堆栈、获取当前的Activity(堆栈最后一个)、结束当前的Activity(堆栈最后一个)、结束指定的Activity、结束指定类名的Activity、结束所有的Activity等方法。

ActivityManager管理类

/**
 * Activity管理类
 */
public class AppManager {

    private static Stack<Activity> activityStack;

    private static AppManager instance;

    private AppManager() {
    }

    /**
     * 单一实例
     */
    public static AppManager getAppManager() {
        if (instance == null) {
            instance = new AppManager();
        }
        return instance;
    }

    /**
     * 添加Activity到堆栈
     */
    public void addActivity(Activity activity) {
        if (activityStack == null) {
            activityStack = new Stack<Activity>();
        }
        activityStack.add(activity);
    }

    /**
     * 获取当前Activity(堆栈中最后一个压入的)
     */
    public Activity currentActivity() {
        Activity activity = activityStack.lastElement();
        return activity;
    }

    /**
     * 结束当前Activity(堆栈中最后一个压入的)
     */
    public void finishActivity() {
        Activity activity = activityStack.lastElement();
        finishActivity(activity);
    }

    /**
     * 结束指定的Activity
     */
    public void finishActivity(Activity activity) {
        if (activity != null) {
            activityStack.remove(activity);
            activity.finish();
            activity = null;
        }
    }

    /**
     * 结束指定类名的Activity
     */
    public void finishActivity(Class<?> cls) {
        for (Activity activity : activityStack) {
            if (activity.getClass().equals(cls)) {
                finishActivity(activity);
            }
        }
    }

    /**
     * 结束所有Activity
     */
    public void finishAllActivity() {
        for (int i = 0, size = activityStack.size(); i < size; i++) {
            if (null != activityStack.get(i)) {
                activityStack.get(i).finish();
            }
        }
        activityStack.clear();
    }

    /**
     * 退出应用程序
     */
    @SuppressWarnings("deprecation")
    public void AppExit(Context context) {
        try {
            finishAllActivity();
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            activityManager.restartPackage(context.getPackageName());
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} 

以上代码来自Android源码设计模式android Activity管理类(全局管理Activity)

使用单例模式来打造ActivityManager类的更多相关文章

  1. PHP用单例模式实现一个数据库类

    使用单例模式的出发点: 1.php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 使用单例模式, 则可以避免大量的new 操作消耗的资源. 2.如果系统中需要有一个类来全局控制某些 ...

  2. 设计模式 - 单例模式mysql数据库操作类

    待续... index.php 调用方法: <?php header('Content-Type:text/html; charset=utf8'); require 'instance.php ...

  3. 利用单例模式设计数据库连接Model类

    之前在<[php]利用php的构造函数与析构函数编写Mysql数据库查询类>(点击打开链接)写过的Mysql数据库查询类还不够完美,利用<[Java]单例模式>(点击打开链接) ...

  4. 单例模式写MySQL model类,简单的增、删、改、查

    单例模式的用途,可用于数据库操作 <?php Class Db { static private $whe;//条件 static private $tab;//表名 static privat ...

  5. PHP单例模式实例,连接数据库对类的引用

    <?php//单例模式连接数据库class pzhang{ static private $instance; private static $config; private $dbase = ...

  6. php单例模式封装数据库操作类增删改查

    <?php//三私一公 单例class Db{ //数据库连接对象 private static $instance; private static $table_name; private $ ...

  7. 类的static成员并用其实现一个单例模式

    对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量.比如说统计某种类型对象已创建的数量.如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这时我们可以用类的静态成员来 ...

  8. [转]单例模式——C++实现自动释放单例类的实例

    [转]单例模式——C++实现自动释放单例类的实例 http://www.cnblogs.com/wxxweb/archive/2011/04/15/2017088.html http://blog.s ...

  9. 设计模式之PHP项目应用——单例模式设计Memcache和Redis操作类

    1 单例模式简单介绍 单例模式是一种经常使用的软件设计模式. 在它的核心结构中仅仅包括一个被称为单例类的特殊类. 通过单例模式能够保证系统中一个类仅仅有一个实例并且该实例易于外界訪问.从而方便对实例个 ...

随机推荐

  1. js数组详解

        1,什么是数组 数组是值得有序集合,每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引.js的数组是无类型的,数组元素可以是任意类型,同一个数组中的不同元素可能是对象或数组 ...

  2. 清楚苹果 iPai端按钮默认样式

    input[type="button"], input[type="submit"], input[type="reset"] { -web ...

  3. Windows操作系统下给文件夹右键命令菜单添加启动命令行的选项

    在命令行中或按下[WIN]+[R]键启动运行对话框的情况下,输入regedit命令启动注册表编辑器,在HKEY_CLASSES_ROOT\Folder\shell下增加一个“CMD”(此处名字可以随便 ...

  4. YoLo 实践(1)

    目录 YoLo 实践(1) 目标: 实施方法: Step 0. 测试项目是否可以正常运行 运行效果图 使用VOC数据结构训练模型 Step1: 生成统一格式的标注文件和类别文件 Step2: 加载预训 ...

  5. 【Markdown】Markdown 使用MathJax引擎 书写Latex 数学公式

    大家都看过Stackoverflow上的公式吧,漂亮,其生成的不是图片.这就要用到MathJax引擎,在Markdown中添加MathJax引擎也很简单, <script type=" ...

  6. 安装busybox玩玩

    到http://www.busybox.net/downloads/binaries/下载放到sdcard然后adb shellsumount -o remount,rw -t yaffs2 /dev ...

  7. 动态注册broadcast的安全考虑

    一.android service通知activity更新方式有1. service 通过广播的形式发送broadcast,向这个activity的内部类发广播的消息来更新界面2. service直接 ...

  8. 深入理解java的形参和实参

    转载声明:本文转载自公众号「码匠笔记」. 前几天在头条上看到一道经典面试题,引发了一些思考.也是写这篇文章的导火索. 背景 请看题: public    classMain{    publicsta ...

  9. java、C语言实现数组模拟栈

    java: public class ArrayStack { private int[] data; private int top; private int size; public ArrayS ...

  10. ASPNET MVC Error 500.19

    今天创建了一个新的ASPNET MVC 项目部署到本地, 生成成功后在浏览器中输入URL却发现报这个错 参照下面的文章我给IIS_IUSRS和IUSR(我比较懒直接everyone)赋予虚拟目录读写权 ...