Android开发经验谈Android技术知识Android开发

设计模式--单例模式

2018-12-04  本文已影响11人  Jackson杰

单例模式是一种最常见的设计模式。

为什么

所谓单例模式,就是一个类仅有一个实例存在。为什么有这种要求呢,因为一个类有多个对象的话,可能会消耗过多的资源,以Android中常用的图片加载框架ImageLoader为例,ImageLoader中包含线程池,缓存系统,网络请求等,很消耗资源,因此没有理由让它构造多个实例,我们在使用的时候,也是通过ImageLoader.getInstance()获得ImageLoader的实例。

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。


要实现单例模式,主要有以下几个关键点:

所有单例模式的UML图如下所示:


UML图比较简单,只有一个实例,提供一个获取实例的方法。

单例模式的写法比较多,关键要知道每种写法的优劣,选择适合自己的写法即可:

写法一,饿汉模式(静态常量,可用)
public class Singleton(){
      private static Singleton instance=new Singleton();
      private Singleton(){
      }
      public static Singleton getInstance(){
        return instance;
      }
    ...
}

优缺点

可以看到这个实例,
优点:在类装载的时候就完成初始化,避免了线程同步问题。
缺点:在类加载的时候创建,即使用不到,也会创建,在一定程度上造成了资源的浪费。

写法二,懒汉模式(线程不安全)
 public class Singleton {
        private static Singleton instance=null;  //利用一个静态变量来记录Singleton的唯一实例
        
        private Singleton() {                //构造方法声明为私有,拒绝外接通过new的方式创建实例
        }
        
        public static Singleton getInstance() {   //提供一个static方法,提供唯一的实例
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
缺点:懒汉形式存在的问题是,在多线程下不能保证实例只有一个。懒汉模式有个判空操作,
if (instance == null) {
                instance = new Singleton();
            }

初始时instance是空的,如果有多个线程同时运行至此,有可能会创建多个实例,instance就不唯一了。所以对于高并发来创建instance的操作,懒汉模式并不实用。

理解懒汉和饿汉

懒汉式,饿汉式这是一种比较形象的称呼。
饿汉式,既然饿,在创建对象的时候比较着急,在装载类的时候就创建对象。
懒汉式,既然懒,在创建实例对象的时候就不着急,会一直等到马上要使用对象实例的时候创建。

补充

懒汉模式和饿汉模式,在创建实例的时候,都有个static关键字。
懒汉模式:

private static Singleton instance=null;

饿汉模式:

private static Singleton instance=new Singleton();

那么这两个static到底哪个用到了static的特性呢。Java中static的特性描述如下:

  1. static变量在类加载的时候初始化。
  2. 多个变量的static变量会公共享同一块内存区域

这样看来,只有饿汉模式用到了static的特性,那么懒汉模式呢,因为懒汉模式中instance用于static修饰的方法中,所以必须要加static。

写法三,懒汉模式(线程安全)(不推荐用)
private static Singleton instance = null;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

可以看到,每次在执行getInstance()方法时,都要进行同步一下。
优点:解决了懒汉模式线程不安全的问题。
缺点:效率变低,每次getInstance()都要同步一下,其实只要初始的时候同步一下就可以,后面要想获取实例的话,只要return就可以了,每次都同步会造成效率变低。

写法四,双重检查(推荐用)
private static volatile Singleton instance;

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

可以看到,有两次检查instnace是否为空的判断,第一次判断是为了解决效率问题,只有当instance为空时,才会同步代码。第二次判空是为了防止出现多个实例。其中volatile关键字是为了防止指令重排。

写法五,静态内部类(推荐用)
private static class SingletonInstance{
        private static final Singleton INSTANCE=new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }

由于SingletonInstance是一个静态内部类,它加载的时机是当它被调用的时候,它在外部类的getInstance()方法被调用,所以只有在getInstance()方法被调用时,才会去加载,达到了延迟加载的目的。
对于内部类SingletonInstance,它采用了类似于饿汉式的单例模式实现,保证了实例的单一。

写法六,枚举
public enum SingleInstance {

    INSTANCE;

    public void doSomething(){
        // do something
        
    }
}

使用

SingleInstance.INSTANCE.fun1();

最简单的单例模式,既保证了线程安全,也没有同步问题,不过在时机项目中,很少见到在实际的项目中运用。

上一篇 下一篇

猜你喜欢

热点阅读