文档章节

设计模式(二)单例模式

 小鲜肉成长记
发布于 2017/07/21 09:49
字数 2053
阅读 3
收藏 0

概念

单例模式也被称作单件模式(单体模式),主要作用是控制某个类型的实例在应用中是唯一的,还提供了一个全局唯一访问这个类实例的访问点getInstance方法。单例模式是对象的创建模式之一,此外还包括工厂模式。

单例模式的特点

  • 该类只有一个实例
  • 该类自行创建实例(改类内部创建自身的实例对象)
  • 想整个系统公开实例接口(类构造方法私有化)
使用范围: 目前java里面实现的单例是一个ClassLoader及其子ClassLoader的访问,也就是说如果一个虚拟机里有多个ClassLoader,而这些ClassLoader都加载某个类的话,就算这个类时单例,也会产生多个实例。如果一个一个机器上有多个虚拟机,那么每个虚拟机里面至少有一个这个类的实例,对于整个机器来说就不是单例了。

使用意义:有些情况需要一个全局变量(如计数器),如果实例多的话,计数会冲突;还有一些配置文件,可能多个地方会用到,如果是不是单例模式,在每个用的地方都创建实例对象的话,系统中会同时存在多份相同的配置文件,这会浪费内存资源。

注意事项:这里讨论的单例不适用于集群环境。

建议单例模式的方法命名为getInstance()。该方法返回值是单例类的类型,方法可以由参数。

单例模式的几种类型

懒汉式单例

类加载的时候不创建实例,只在第一次请求实例的时候创建,并且只创建一次,但由于线程同步会降低访问速度。

/**
 * 懒汉式单例模式示例
 */
class LazySingleton{
    //私有静态对象,类加载的时候不做初始化
    private static LazySingleton instance = null;
    //私有构造方法,避免外部创建实例
    private LazySingleton(){}
    //静态工厂方法,返回此类的唯一实例,实例没有初始化时才初始化
    synchronized public static LazySingleton getInstance(){
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

  • 构造方法私有化:避免类外部通过构造方法创建多个实例
  • 提供获取实例的方法:构造方法私有化后外部不能创建实例,此时,让类提供一个方法来返回类的实例
  • 获取实例的方法是静态的:客户端需要调用这个方法就要的先得到类实例,可是这个方法就是为了返回类实例,避免武侠循环,在该方法添加static关键字,就可以通过类名直接访问
  • 定义好存储实例的属性:如果直接 return new Singlenton() 返回实例,每次客户端调用时,都将产生一个新的实例,这样肯定会有多个实例。单例模式可以用一个属性来记录创建好的类实例,第一次创建时记录下来,以后就可以复用
  • 把这个属性也定义成静态的:由于在一个静态方法里使用,这一这个属性被迫成为一个类变量
  • 线程安全:降低访问速度,而且每次访问都需要判断一次。为了更好的实现,采用“双重检查加锁”(不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后先检查实例是否存在,如果不存在才进行下面的同步块,,这是第一重检查,进入同步块后,再次检查实例是否存在。如果不存在,就在同步的情况下创建实例,这是第二重检查。这样的话整个过程只需要一次同步,从而减少了多次在同步情况下进行判断所浪费的时间)
  • 使用“双重检查加锁”机制时,需要添加volatile关键字,使用在Java5以上版本、
class LazySingleton{
    private volatile static LazySingleton instance = null;
    private LazySingleton(){}
    public static LazySingleton getInstance(){
        if (instance == null) {
            synchronized (LazySingleton.class){
                if(instance == null){
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

饿汉式单例

类被加载的时候唯一的实例已经被创建,不能实现延迟加载

/**
 * 饿汉式单例模式示例
 */
class Singleton{
    //私有静态变量存储创建好的实例
    private static Singleton instance = new Singleton();
    //private static final Singleton instance = new Singleton();可以定义为static final成员
    //私有构造方法,避免外部创建实例
    private Singleton(){}
    //微客户端提供类实例
    public static Singleton getInstance(){
        return instance;
    }
}

存储实例的属性是静态的,利用了static的特性

  • static变量在类装载的时候进行初始化
  • 多个实例的static变量会共享同一块内存区域

登记式单例

维护的是一组单例类的实例,将这些实例存放在一个map(登记簿),已登记的实例,从工厂直接返回,没登记的,则先登记再返回。

public class Singleton {
    //登记簿,用来存放所有登记的实例
    private static Map<String, Singleton> registry = new HashMap<>();
    //在类加载的时候添加一个实例到登记簿
    static {
        Singleton x = new Singleton();
        registry.put(x.getClass().getName(), x);
    }
    //受保护的默认构造方法
    protected Singleton(){}
    //静态工厂方法,放回指定等级对象的唯一实例
    public static Singleton getInstance(String name){
        if(name == null){
            name = "Singleton";
        }
        if (registry.get(name) == null){
            try {
                registry.put(name, (Singleton) Class.forName(name).newInstance());
            } catch (InstantiationException e){
                e.printStackTrace();
            } catch (ClassNotFoundException e){
                e.printStackTrace();
            } catch (IllegalAccessException e){
                e.printStackTrace();
            }
        }
        return registry.get(name);
    }
    
}

另一种实现单例模式的方式

常见的两种实现方式都存在小小的缺陷,既能实现延迟加载,又能实现线程安全的方式 Lazy initialization holder class 模式,该模式综合使用了java的类级内部类和多线程默认同步锁的知识,巧妙的实现了延迟加载和线程安全。

  • 静态初始化器方式:简单的实现了线程安全,可以由JVM保证线程安全性,但这种方式会在类装载的时候初始化对象,不管你需不需要,浪费一定的空间。
  • 类级内部类:能够让类装载的时候不去初始化对象,在这个类级内部类里去创建对象实例,只要不适用内部类,就不会创建对象实例。
public class SingletonPattern {
    /**
     * 类级内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     * 没有绑定关系,而且只有被调用的时候才会被装载,从而实现了延迟健在
     */
    private static class SingletonHolder{
        //静态初始化器,由JVM保证线程安全
        private static SingletonPattern instance = new SingletonPattern();
    }
    private SingletonPattern(){}
    
    public static SingletonPattern getInstance(){
        return SingletonHolder.instance;
    }    
}

当getInstance方法第一次被调用时,它第一次读取SingletonHolder.instance,使SingletonHolder类得到初始化,这个类被装载并初始化的时候,会初始化它的静态域,从而创建SingletionPattern实例,由于是静态域,只有在虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全。

单例模式优缺点

  • 内存中只有一个实例,减小了内存消耗,减少系统性能开销,例如读写配置
  • 避免对资源的多重占用,例如文件的读写操作
  • 可以在系统设置全局的访问点,优化共享资源的访问
  • 单例模式没有接口,扩展困难,如果要扩展就要修改代码
  • 不利于测试,如果在并行开发环境中,单例模式没有完成,是没办法进行测试的,不能通过接口或者mock的方式虚拟对象
  • 没有实现单一只能原则,把“要单例”和业务逻辑融合在一起

使用场景

  • 要生成唯一序列号
  • 整个项目中需要一个共享访问点或共享数据,如web页面的计数器
  • 创建一个对象需要消耗的资源过多,如要访问I/O,访问数据库等资源
  • 需要定义大量的静态常量和静态方法(如工具类)

© 著作权归作者所有

粉丝 0
博文 12
码字总数 14860
作品 0
东城
私信 提问
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
2018/06/24
0
0
设计模式已经陨落了?

你现在是坐在一个程序员旁边吗?如果是的话,那么在你读下面的段落之前,有一个简单的实验。让他们到一边去,问问他们两个问题并记录下答案。首先问他们“什么是设计模式?”然后再问“说出你...

oschina
2014/03/11
9.4K
69
炒冷饭系列:设计模式 抽象工厂模式

炒冷饭系列:设计模式 抽象工厂模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 亲爱我,孝何难;亲恶我,孝方贤。 一、什么是抽象工厂模...

泥沙砖瓦浆木匠
2014/07/24
139
0
PHP设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式。 一、设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用、容易被他人理解的、可靠的代码设...

juhenj
2014/05/15
280
2
《PHP设计模式大全》系列分享专栏

《PHP设计模式大全》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第1/3页 php设计模式介绍之值对象模式第1/5页...

kaixin_code
2018/11/06
170
0

没有更多内容

加载失败,请刷新页面

加载更多

rime设置为默认简体

转载 https://github.com/ModerRAS/ModerRAS.github.io/blob/master/_posts/2018-11-07-rime%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%BB%98%E8%AE%A4%E7%AE%80%E4%BD%93.md 写在开始 我的Arch Linux上......

zhenruyan
今天
5
0
简述TCP的流量控制与拥塞控制

1. TCP流量控制 流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。 原理是通过确认报文中窗口字段来控制发送方的发送速率,发送方的发送窗口大小不能超过接收方给出窗口大小。...

鏡花水月
今天
9
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
964
11
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部