美文网首页
设计模式学习-单例模式

设计模式学习-单例模式

作者: m1Ku | 来源:发表于2018-12-16 16:09 被阅读0次

定义

单例模式是应用最广泛的设计模式之一。其确保单例类只有一个实例,而且自行实例化并向系统提供这个实例。当创建一个对象需要消耗很多资源时,可以考虑使用单例模式。

UML类图

单例模式

单例模式的角色

  • Client

    客户端类,即单例类的使用者。

  • Singleton

    单例类,向客户端提供单例对象的实例。

单例模式的实现关键点

  1. 私有化单例类的构造函数,确保客户端不能通过new的方式实例化对象。
  2. 一般提供一个静态方法向客户端提供对象的实例。
  3. 确保单例类的对象有且只有一个,尤其在多线程情况下。

单例模式的实现方式

1.饿汉式

饿汉式在定义静态单例对象时就将其初始化,并在静态方法中返回。

/**
 * 单例
 * 饿汉式
 */
public class Singleton {
    //声明静态对象并初始化
    private static Singleton instance = new Singleton();
    //私有化构造函数
    private Singleton(){

    }
    //通过静态方法返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}

2.懒汉式

懒汉式先定义静态单例对象,在单例类第一次使用时初始化对象,getInstance为同步方法,保证了多线程情况下单例对象的唯一性。

/**
 * 单例
 * 懒汉式
 */
public class Singleton {
    private static Singleton instance;
    private Singleton(){

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

该方式的优点是,单例对象在第一次使用时才初始化。其缺点是,每次调用getInstance方法都会进行同步,造成不必要的开销。

3.Double Check Lock(DCL)双重检查锁实现单例

/**
 * 单例
 * 双重检查
 */
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {

    }

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

DCL是开发中使用最多的实现单例的方式。getInstance中有两次判空,故其名曰双重检查。第一次判空是为了避免不必要的同步,第二次判空为了确保在实例为空的情况下再初始化单例对象。

但由java的指令重排序,可能会导致DCL检查失效,所以静态变量上要加volatile关键字确保安全。

4.静态内部类实现单例

/**
 * 单例
 * 静态内部类实现
 */
public class Singleton {
    private Singleton(){

    }

    private Singleton getInstance(){
        return SingletonHolder.instance;
    }

    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
}

第一次加载Singleton时并不会初始化instance,只有在第一次调用getInstance方法时会导致instance被初始化。这种方式不仅确保了线程安全,也保证了单例对象的唯一性,而且还延迟了单例的初始化。

5.通过枚举实现

/**
 * 单例
 * 定义枚举类实现
 */
public enum  Singleton {
    INSTANCE;
}

默认枚举实例的创建是线程安全的,在任何情况下它都是一个单例。对于其他几种实现单例的方式在反序列化时会重新创建对象,而枚举不存在这个问题。

6.使用容器实现单例模式

public class SingletonManager {
    private static HashMap<String, Object> sMap = new HashMap<>();

    private SingletonManager() {

    }

    public void registerService(String key, Object value) {
        if (!sMap.containsKey(key)) {
            sMap.put(key, value);
        }
    }

    public Object getService(String key) {
        return sMap.get(key);
    }
}

这种方式可以将多种单例类型注入到统一的管理类中,在使用时根据key获取对应的单例类对象,在使用时通过统一的接口获取,降低了用户使用成本。

Android源码中的单例模式

LayoutInflater是Android提供的负责加载布局的服务,它也是一个单例。我们会通过LayoutInflater.from(this)获得它的实例,from方法如下

   /**
     * Obtains the LayoutInflater from the given context.
     */
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

在该方法中通过context的getSystemService来获得LayoutInflater实例,在分析装饰模式时,我们知道Context的实现类是ContextImpl,我们到ContextImpl中找到getSystemService方法

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

这里调用了SystemServiceRegistry对象的getSystemService方法

/**
 * Gets a system service from a given context.
 */
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

在这个方法中首先会获得ServiceFetcher对象,然后从fetcher中获得service对象。SYSTEM_SERVICE_FETCHERS是一个key为String,value为ServiceFetcher的HashMap,那现在的问题就是要搞明白ServiceFetcher对象是如何存到SYSTEM_SERVICE_FETCHERS这个Map中的。SystemServiceRegistry中的registerService方法如下

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

就是在这个方法中调用了SYSTEM_SERVICE_FETCHERS的put方法将ServiceFetcher对象存入。而registerService方法又在SystemServiceRegistry的静态代码块中调用,如下

static {
//...
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
//...
}

那么当SystemServiceRegistry类加载时,这里的静态代码块执行,并完成各个系统服务的注册,由于静态代码块只执行一次,所以也满足了单例的要求。

相关文章

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

  • python 单例

    仅用学习参考 目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计...

  • iOS-单例模式

    swift的单例设计模式 OC的单例设计模式 新学习一种单例思想

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • 设计模式系列教程之单例模式-原理介绍

    设计模式系列教程之单例模式-原理介绍 一:单例模式(Singleton)学习步骤 经典的单例模式原理: 本文出处:...

  • 【Java】设计模式 —— 深入浅出单例模式

    学习笔记 参考:深入浅出单实例SINGLETON设计模式单例模式【Java】设计模式:深入理解单例模式 场景:一般...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • JAVA中各种单例模式的实现与分析

    单例模式是学习设计模式过程中最基本的一个设计模式,基本上一开始学习就会学到单例模式,实际上在java中实现单例模式...

  • 【设计模式】单例模式

    学习文章 iOS设计模式 - 单例 SwiftSingleton 原理图 说明 单例模式人人用过,严格的单例模式很...

网友评论

      本文标题:设计模式学习-单例模式

      本文链接:https://www.haomeiwen.com/subject/rllxkqtx.html