博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式【单例模式】
阅读量:7217 次
发布时间:2019-06-29

本文共 3117 字,大约阅读时间需要 10 分钟。

单例设计模式是设计模式中使用最为普通的模式之一。它是一种对象创建模式,用于产生一个对象的实例,它可以确保系统中一个类只产生一个实例。在Java语言中,这样的行为能带来两大好处:

  (1)对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。

  (2)由于new操作的次数越少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。

因此对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效地改善系统的性能。

单例模式的参与者和参与者非常简单,只有单例类和使用者两个,如下图:

  单例模式的核心在于通过一个接口返回唯一的对象实力。一个简单的单例实现

public class Singleton {	private Singleton(){		System.out.println("Singleton is create.");//创建单例的过程可能会比较慢	}	private static Singleton instance = new Singleton();	public static Singleton getInstance(){		return instance;	}}

  注意代码中标注红色的部分,首先单例类必须要有一个private访问级别的构造函数,只有这样,才能确保单例不会在系统中的其他代码内被实例化,这是相当重要的;其次instance成员变量和getInstance()方法必须是static的。

  这种单例的实现方式很简单,而且十分可靠。他唯一不足仅是无法对instance实例做延迟加载。假如单例的创建过程很缓慢,而由于instance成员变量是static定义的,因此在JVM加载单立时,单例对象就会被建立,如果此时这个单例在系统中汉扮演其他角色,那么在任何使用这个单例的地方都会初始化这个单例变量,而不管是否会被用到。比如单例类作为String工厂,用于创建一些字符串(该类及用于创建单例Singleton,又用于创建String对象):

public class Singleton {	private Singleton(){		System.out.println("Singleton is create.");//创建单例的过程可能会比较慢	}	private static Singleton instance = new Singleton();	public static Singleton getInstance(){		return instance;	}	public static void createString(){//这是单例扮演其他角色		System.out.println("createString in Singleton.");	}}

  当时用Singleton. createString()执行任务时,程序输出如下图:

  从输出结果可以看到,虽然此时并没用使用单例类,但它还是被创建出来,这也许是开发人员不愿意看到的。为了解决这个问题,并以此提高系统在相关函数调用时的反应速度,就需要引入延迟加载机制。

public class LazySingleton {	private LazySingleton(){		System.out.println("LazySingleton is create.");//创建单例的过程可能会比较慢	}	private static LazySingleton instance = null;	public static synchronized LazySingleton getInstance(){		if(instance==null){			instance = new LazySingleton();		}		return instance;	}}

  首先,对于静态成员变量instance初始值赋予null,确保系统启动时没有额外的负载;其次,在getInstance()工厂方法中,判断当前单例是否存在,若存在则返回,不存在则再创建单例。这里尤其还要注意,getInstance()方法必须是同步的,否则在多线程环境下,当线程1正再创建单例时,完成赋值操作前,线程2可能判断instance为null,故线程2也将启动新建单例的程序,而导致多个实例被创建,故同步关键字是必须的。

  使用上例中的单例实现,虽然实现了延迟加载的功能,但和第一种方法相比,它引入了同步关键字,因此在多线程环境中,它的时耗要远远大于大于第一种单例模式。

  为了使用延迟加载引入同步关键字反而降低了系统性能,是不是有点得不偿失?为了解决这个问题,还需要对其进行改进:

public class StaticSingleton {	private StaticSingleton(){		System.out.println("StaticSingleton is create.");	}	private static class SingletonHolder{		private static StaticSingleton instance = new StaticSingleton();	}	public static StaticSingleton getInstance(){		return SingletonHolder.instance;	}}

  在这个实现中,单例模式使用内部类来维护单例的实例,当StaticSingleton被加载时,其内部类并不会被初始化,故可以确保当StaticSingleton类被载入JVM时,不会初始化单例类,而当getInstance()方法被调用时,才会加载SingletonHoider,从而初始化instance。同时,由于实例的建立是在类加载时完成,故天生对多线程友好,getInstance()方法也不需要使用同步关键字。因此,这种实现方式同时兼备以上两种实现的优点。

  通常情况下,用以上方式实现的单例已经可以确保在系统中只存在唯一实例了。但仍然有例外情况,可能导致系统生成多个实例,比如在代码中,通过反射机制,强行调用单列类的私有构造函数,生成多个单例。

  一个可以被串行化的单例:

public class SerSingleton implements Serializable{	String name;		private SerSingleton(){		System.out.println("SerSingleton is create.");		name = "SerSingleton";	}		private static SerSingleton instance = new SerSingleton();		public static SerSingleton getInstance (){		return instance;	}		public static void createString(){		System.out.println("createString in Singleton.");	}		private Object readResolve(){		return instance;	}}

  

 

转载于:https://www.cnblogs.com/wudi521/p/7381969.html

你可能感兴趣的文章
细讲递归(recursion)
查看>>
进程和进程间通信
查看>>
微处理器的两种结构比较
查看>>
ORACLE EXPIRED(GRACE)
查看>>
Markdown应用样例
查看>>
多文本框的值得存放和赋值
查看>>
Linux中计划任务执行脚本crontab-简洁版
查看>>
Java - IO
查看>>
安卓app中嵌入一个H5页面,当手机系统设置字体变大时,如何使H5页面的字体不会随用户自己调整的系统字体变化而变化?...
查看>>
safari 收藏导出 手机safari 导出
查看>>
Dalvik 虚拟机 jvm 区别
查看>>
hexo从零开始
查看>>
币值转换
查看>>
RabbitMQ(六)远程连接
查看>>
得到ios设备的一些信息
查看>>
【教程】如何修改路由表?
查看>>
云计算与虚拟化之后:网络威胁成新挑战
查看>>
50个Demo展示HTML5无穷的魅力
查看>>
chapter 4:贪心
查看>>
批处理学习笔记
查看>>