单例(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. PAT 1028. List Sorting

    #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> ...

  2. iview table数据排序不正确

    在使用iview的table组件时,要做排序 代码: sortable: true,sortMethod: (a, b, type) => { if (type === 'desc') { re ...

  3. Apache服务器运维笔记(4)----服务器扩展部分

    在Apache的默认配置文件夹中有一个 extra 目录,这个目录是用来存放 Apache 其他模块的配置文件的.这些文件是 Apache 针对常用的模块而设置并提供的,它们都是通过 Include ...

  4. 多盟、Testin云测、K9test,助阵阿里云1218 移动开发者狂欢

    经过双十一.双十二全民剁手狂欢后,阿里巴巴旗下的云计算业务,也为IT程序员们打造一场独特的盛宴. 阿里云计算12月18日对旗下主力云计算产品进行打折促销,云服务商.风投机构等都将参与到这场狂欢中.“我 ...

  5. Python unittest模块心得(一)

    关于unittest模块的一些心得,主要是看官网的例子,加上一点自己的理解,官网地址:https://docs.python.org/3.6/library/unittest.html 基础概念介绍: ...

  6. 如何一次性下载某个类库依赖的所有jar包"

    ** 经常碰到这种事情: ** 在一些非maven工程中(由于某种原因这种工程还是手工添加依赖的),需要用到某个新的类库(假设这个类库发布在maven库中),而这个类库又间接依赖很多其他类库,如果依赖 ...

  7. SQL Server ->> Database Snapshot(数据块快照)

    Comming soon!!! 参考文献: View the Size of the Sparse File of a Database Snapshot 数据库快照 (SQL Server) 创建数 ...

  8. [问题记录]cocos2dx编译打包apk过程&问题记录

    目录: 1. 入门 2. 编译 3. 问题 4. 总结 5. 参考 ------------------------------------------------------------------ ...

  9. jave web 监听器。

    https://www.imooc.com/video/5664 Web监听器由Servlet规范提供的,可以监听客户端的请求以及服务端的操作,即监听ServletContext.HttpSessio ...

  10. TFS--解决新创建的windows用户无法访问TFS的问题

    今天入职新同事,帮忙配置TFS的账号碰到一个问题,TFS账号是映射取administrators组得 所以新建用户之后,无法马上引入TFS.查询原因是 Builtin组中没有该账号,以前也总是碰到新加 ...