单例(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. 牛顿迭代,多项式求逆,除法,开方,exp,ln,求幂

    牛顿迭代 若 \[G(F_0(x))\equiv 0(mod\ x^{2^t})\] 牛顿迭代 \[F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}(mod ...

  2. [小北De编程手记] : Lesson 04 - Selenium For C# 之 API 上

    这一部分,我准备向大家介绍Selenium WebDriver的常用API,学习这部分内容需要大家最好有一些简单的HTML相关知识,本文主要涉及到以下内容: Selenium API:元素检查 Sel ...

  3. 讲解JavaScript两个圆括号、自调用和闭包函数

    一.JavaSript圆括号的使用 先来看一组通过函数声明来定义的函数: 先附代码: 运行结果如下: 这里我们可以看出: Ø  若没有加圆括号,则返回的是这个函数的内容 Ø  若加上圆括号,则返回的是 ...

  4. CentOS 7 禁用IPV6以提高网速

    方法 1 编辑文件/etc/sysctl.conf,$vi /etc/sysctl.conf添加下面的行: net.ipv6.conf.all.disable_ipv6 = net.ipv6.conf ...

  5. MySQL主从复制与读写分离概念及架构分析

    1.MySQL主从复制入门 首先,我们看一个图: 影响MySQL-A数据库的操作,在数据库执行后,都会写入本地的日志系统A中. 假设,实时的将变化了的日志系统中的数据库事件操作,在MYSQL-A的33 ...

  6. Tesseract-OCR-04-使用 jTessBoxEditor 进行训练

    Tesseract-OCR-04-使用 jTessBoxEditor 进行训练 本篇是关于 jTessBoxEditor 进行训练,使 Tesseract-OCR 文字识别准确率得到极大的提高,本篇完 ...

  7. Java在Web开发语言上败给了PHP(转)

    PHP的主要语言开发者之一.Zend公司的创始人之一Andi Gutmans最近在blog中直言不讳地批评了Java语言.他指出,目前Java厂商试图在JVM上提供动态语言实现的路子根本不对,Java ...

  8. react-native-splash-screen

    react-native-splash-screen在GitHub上的地址:https://github.com/crazycodeboy/react-native-splash-screen rea ...

  9. Linux ->> scp命令复制对端机器上的文件/文件夹

    scp是secure copy的简写,用于在Linux下从远程机器拷贝文件. 特点: 传输是加密的,稍微影响了一下速度.而相比较rsync,它对于资源的占用还是有优势的. 用法 scp [参数] [原 ...

  10. dctcp-2.6.26-rev1.1.0.patch

    dctcp-2.6.26-rev1.1.0.patch diff -Naur linux-/include/linux/sysctl.h linux--dctcp-rev1.1.0/include/l ...