单例(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. Recursive sum in parent-child hierarchy T-SQL

    ---树形(父子关系类)分级类统计(父子统计) --涂聚文 2014-08-14 drop table BookKindList create table BookKindList ( BookKin ...

  2. curl POST JSON

    1. 场景 Controller接收json格式数据 封装bean @RequestMapping(value = "/bb", method = RequestMethod.PO ...

  3. [ERROR] Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:2.8:unpack (unpack) on project sq-integral-web: Unable to find artifact.

    1.问题描述 项目maven打包报上述错误, 但是小伙伴运行好使. 2.问题解决 是idea工程编码(gbk)和项目编码(utf-8)不一致 idea->file->Other Setti ...

  4. cf1037E. Trips(图论 set)

    题意 题目链接 Sol 倒着考虑!倒着考虑!倒着考虑! 显然,一个能成为答案的子图一定满足,其中任意节点的度数\(>= k\) 那么倒着维护就只用考虑删除操作,如果一个点不合法的话就把它删掉,然 ...

  5. var a =10 与 a = 10的区别

    学习文章------汤姆大叔-变量对象 总结笔记 变量特点: ①变量声明可以存储在变量对象中.②变量不能直接用delete删除. var a =10 与 a = 10的区别: ①a = 10只是为全局 ...

  6. vue中数组删除,页面没重新渲染

    创建一个组件时,数据类型是数组,在删除这个数组中的数据时,数组中的数据是对的,但页面渲染的数据却不对. 举例:(不一定复现) <ul> <li v-for="(item, ...

  7. RocketMQ读书笔记4——NameServer(MQ的协调者)

    [NameServer简述] 对于一个消息队列集群来说,系统由很多机器组成,每个机器的角色.IP地址都不相同,而且这些信息是变动的(如在某些情况下,会有新的Producer或Consumer加入). ...

  8. 常量、变量、数据类型 搞错N+1次 累死

    public class hello { /** * * * * * @param args */ public static void main(String[] args) { String _$ ...

  9. SQLServer中取当前年,月,日,时,分,秒

    Select GETDATE() as '当前日期',DateName(year,GetDate()) as '年',DateName(month,GetDate()) as '月',DateName ...

  10. 关于安卓开发的学习一:webview

    在网上看到几篇不错的博客,分享和学习一下! Android使用WebView加载网页 https://blog.csdn.net/tuke_tuke/article/details/51684254 ...