单例模式是一种常用的软件设计模式,通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问。

使用说明

1、使用场景

  在某些系统中某些对象最多只能存在一个,例如Windows中只能打开一个任务管理器,一个系统只能有一个计时工具或序号生成器,此时,建议使用单例模式。

2、要点

  1) 单例模式的类只提供私有的构造函数;

  2) 类定义中含有一个该类的静态私有对象;

  3) 类提供了一个静态的共有的函数用户创建或获取它本身的静态私有对象;

单例模式在Java中的几种创建模式与优缺点分析

1、懒汉模式

  所谓的懒汉模式其实是一种比较形象的称谓,他的懒主要表现是在类装载的时候并不着急创建对象实例,等到需要实例时再去创建,是懒加载的一种表现。懒汉模式是一种典型的以时间换空间的做法,在每次获取实例的时候都会进行判断,看是否需要创建实例,浪费了每次判断的时间,优点是没人使用实例时可节约内存空间。

  1) 线程不安全的懒汉模式。优点:具备lazy loading  缺点:不支持多线程   推荐指数:不推荐   

public class Singleton {
private static Singleton instance;
//私有构造函数 防止类在外部被实例化
private Singleton(){}
//静态工厂方法
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}

  这种单例模式是初学者最先想到的一个设计模式,获取实例时先执行判断,在单线程程序中可能是一个不错的设计模式,但是在多线程程序中,它无法正常工作,因此不推荐使用。

  2) 线程安全的懒汉模式。优点:具备lazy loading,支持多线程  缺点:多线程下每次获取实例都需同步,效率很低   推荐指数:★  

public class Singleton {
private static Singleton instance;
//私有构造函数 防止类在外部被实例化
private Singleton(){}
//静态工厂方法
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}

  相比上一种设计模式,增加了关键字synchronized,可以保证该设计模式在多线程下能够工作而不发生混乱。但在多线程工作环境中,每个线程调用方法getInstance都会同步,因此会大大降低程序执行效率。

2、饿汉模式

  所谓的饿汉模式是一种比较形象的称谓,因为饿,所以在类初次装载时就着急创建了类的实例。饿汉模式是一种典型的以空间换时间的做法,当类装载时就会创建类的实例,不管你装载类的时候是否使用实例,他都会创建出来,此后获取实例时就无需判断是否存在,节省了运行时间,且能够良好地支持多线程环境。缺点是,在理论上当实例对象很大时,过早地创建实例可能会浪费一定的内存空间,但笔者认为,在通常情况下无需考虑这种情况。

  标准饿汉模式。优点:编写简单,良好地支持多线程且运行效率很高  缺点:不具备lazy loading,因此理论上在某种情况下会浪费一定的内存空间   推荐指数:★

public class Singleton {
//写法1
private static Singleton instance = new Singleton();
//写法2
//private static Singleton instance = null;
//static{
// instance = new Singleton();
//} //私有构造函数 防止类在外部被实例化
private Singleton(){}
//静态工厂方法
public static Singleton getInstance(){
return instance;
}
}

  我们知道,当整个程序运行时,静态类Singleton类并不会立即被装载,因此其实例也不会立即被初始化。只有在某处使用到了Singleton类时,它才会被装载。因此在理论上,饿汉模式的唯一缺点成立需要以下两个条件:①类对象非常占用内存 ②类体内除了方法getInstance还有其他静态公有方法或静态变量,且这些方法或变量需在调用方法getInstance前使用。在上述两个条件均成立时,饿汉模式所表现出的缺点也仅是在一定时间内浪费了内存空间,这里的时间间隔指的是从调用条件②中的方法或变量开始到调用方法getInstance的时间间隔。综上,这种单例模式编写简单且能够在多线程环境中高效地运行,笔者认为近乎完美,如果不是过分追求设计360度无死角,可以毫不夸张地说,饿汉模式适用于任何条件。

3、 双重校验锁

  双重校验锁是在线程安全的懒汉模式的一个升级版,由于线程安全的懒汉模式在多线程环境下运行性能较低,双重校验锁在上述基础上进行改进,只在类实例未初始化时加锁初始化过程,待类实例初始化后,获取类实例并不会执行同步操作,因此大大提高了执行效率。

  双重校验锁模式。优点:具备lazy loading节省空间,良好地支持多线程且运行效率较高  缺点:编写稍复杂,volatile关键字会影响运行效率   推荐指数:★

public class Singleton {
//注意此处关键字volatile,使用该关键字,变量instance将不会被本地线程缓存,所有对该变量的读写操作都是直接操作共享内存,从而确保该变量的值最新且正确
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
//先检查实例是否存在,如果不存在才会进入下面的同步代码块
if(instance==null){
synchronized(Singleton.class){
//需再次检查实例是否存在,如果不存在才真正创建
//有可能一个线程获取instance时为null,但进入当前同步代码块后instance已经被其他线程创建,因此需判断是否重复
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}

  注意,这里必须使用volatile关键字,因为instance = new Singleton()不是一个原子操作,实际执行时可能被指令重排,导致外部获取到初始化不完整的instance。

  这种设计模式既可以实现线程安全地创建实例,而又不会对性能造成很大的影响,仅仅在初始化类实例时需要同步,此后获取实例都不需要同步操作。在上述代码实现中,可以看到使用了关键字volatile,被该关键字修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。但是,由于volatile关键字可能会屏蔽虚拟机中一些必要的代码优化,所以会在一定程序上影响运行效率,因此对于双重校验锁模式,并不建议大量使用。

4、静态内部类

  静态内部类是饿汉模式的升级版,由于饿汉模式理论上在某些条件下存在缺点,该缺点产生的原因是类体内除了getInstance方法还存在其他公有静态方法或变量,在调用这些方法或变量时,类会被装载,同时类实例也会被立即初始化,我们可以将Singleton的实例存放于一个内部类SingletonHolder中,只有当调用Singleton获取实例对象方法时,才会装载内部类SingletonHolder,很好地解决了饿汉模式存在的缺点。

  静态内部类模式。优点:具备lazy loading节省空间,完美地支持多线程且运行效率很高  缺点:暂无   推荐指数:★

public class Singleton {
//私有构造函数
private Singleton(){}
//类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
private static class SingletonHolder{
//静态初始化器,由JVM来保证线程安全
private static Singleton instance = new Singleton();
}
//静态工厂方法
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}

  相比上述其他单例模式的实现方式,静态内部类实现方法具备他们拥有的任何优点(lazy loading懒加载,支持多线程并且高效),因此,静态内部类是一种比饿汉模式更完美的单例实现方式。

5、枚举

  在《高效Java第二版》中,作者提到了使用枚举类型来实现单例模式,实现起来更加简洁、高效和安全,防止了多次实例化,给出的示例代码如下所示。

public enum Singleton {
//定义一个枚举的元素,它就代表了Singleton的一个实例。
uniqueInstance; //单例可以有自己的操作
public void singletonOperation(){
//功能处理
}
}

  对于这种实现方式,写法超级简单,又能解决大部分的问题。

参考资料

  《我们再来聊一聊Java的单例吧

设计模式之 -- 单例模式(Singleton)的更多相关文章

  1. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  2. 设计模式(4) -- 单例模式(Singleton)

    设计模式(4)  -- 单例模式(Singleton) 试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说 ...

  3. 乐在其中设计模式(C#) - 单例模式(Singleton Pattern)

    原文:乐在其中设计模式(C#) - 单例模式(Singleton Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 单例模式(Singleton Pattern) 作者:weba ...

  4. 【设计模式】单例模式-Singleton

    [设计模式]单例模式-SingletonEnsure a class has only one instance, and provide a global point to access of it ...

  5. 设计模式之——单例模式(Singleton)的常见应用场景

    单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...

  6. 设计模式之单例模式(Singleton Pattern)

    单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...

  7. 设计模式一: 单例模式(Singleton)

    简介 单例模式是属于创建型模式的一种(另外两种分别是结构型模式,行为型模式).是设计模式中最为简单的一种. 英文单词Singleton的数学含义是"有且仅有一个元素的集合". 从实 ...

  8. 设计模式之——单例模式(Singleton)的常见应用场景(转):

    单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...

  9. java设计模式之 单例模式 Singleton

    static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...

  10. 【设计模式】单例模式 Singleton Pattern

    通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance)  的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...

随机推荐

  1. RT-Thread信号量实际运用—按键点灯

    上面是魔笛开发板上 LED 和按键的 IO 分布,我们通过信号量的方法来同步按键线程和LED 线程,实现当 enter 键按下后,点亮或关闭 LED 的动作. /******************* ...

  2. DateTime Related Functions

    string a = "to_date('" + dtpStart.Value.ToString("yyyy/MM/dd") + "', 'yyyy/ ...

  3. 初学Java,第一段代码

    public class myapp { public static void main(String[] args) { // TODO Auto-generated method stub Sys ...

  4. Calculate its MTBF assuming 2000 FITS for each DRAM

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION A common unit of meas ...

  5. physical addressing virtual addressing 物理寻址 虚拟寻址

    Computer Systems A Programmer's Perspective Second Edition The main memory of a computer system is o ...

  6. changing a pointer rather than erasing memory cells

    Computer Science An Overview _J. Glenn Brookshear _11th Edition In a data structure based on a point ...

  7. C#读取shapefile文件(不用AE)

    1.创建工程文件,选窗体应用程序,命名为:EsriShpReader 2.添加SplitContainer控件到窗体 3.在SplitContainer.panel1中添加两个按钮Button,tex ...

  8. 页面瀑布流布局的实现 javascript+css

    先看所谓的瀑布流布局 在不使用瀑布流布局的情况下,当页面要显示不同高度的图片时,会如下面显示 下面的元素总是和最靠近它的元素对齐. 为了使元素能够在我们想要的位置上显示,我们使用绝对定位. 说一下大体 ...

  9. Java构造和解析Json数据的两种方法详解二

    在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Jso ...

  10. synchronized的使用方法

    [转自] http://blog.csdn.net/witsmakemen/article/details/6966116 记下来,很重要. Java语言的关键字,当它用来修饰一个方法或者一个代码块的 ...