单例模式
Posted mo-lu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。
单例模式是一种常见的设计模式,其核心为“每个类只能有一个实例”。通过对类中对象实例化的控制,确保某些场景下状态的唯一性。在系统中的某些类只有一个实例非常重要,如打印任务、一个窗口管理器或文件系统等。
背景
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。例如:系统中可以存在多个打印任务,但只能用一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器;一台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况;一台计算机可以有若干通信端口,但是系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用等。因此,单例模式可以被广泛的应用的下述环境中:
- 要求生成唯一序列号的环境;
- 整个项目中需要一个共享访问点或共享数据。如,一个web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,确保线程安全;
- 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
- 需要定义大量的静态常量和静态方法(如工具类)的环境,采用单例模式(或static声明);
一、定义
单例模式(Singleton Pattern)即某各类中只有一个实例,且自行实例化并向整个系统提供这个实例。
二、特点
- 单例类只能有一个实例。
- 单例类必须自己自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
三、实现
举例:一个国家只能有一个皇帝(即单例类);
3.1 单例模式通用
例1: /* 单例模式通用代码*/ package simplePattern; public class Singleton { //定义各一个单例类 private static final Singleton singleton = new Singleton(); //定义私有的构造函数,限制一个类只能产生一个对象 private Singleton(){ } public static Singleton getSingleton(){ return singleton; } //类中其他方法能够其他类调用 public static void doSomething(){ } }
然而,单例模式也存在一些缺点。如:
- 一般没有接口,扩展困难;
- 不利于测试。在并发环境中,如果单例模式没有完成,不能进行测试,没有接口也无法使用mock的方式虚拟一个对象。
- 单例模式与单一职责原则有冲突。单例模式中把“要单例”和业务逻辑融合在一个类中
因此,针对具体情况可以对单例模式进行改进。
3.2 高并发下单例模式实现
例2: public class Singleton_hignconcur { private static Singleton_hignconcur singleton= null; //定义私有的构造函数,限制一个类只能产生一个对象 private Singleton_hignconcur(){ } //方法 public static Singleton_hignconcur getSingleton(){ if(singleton==null){ singleton=new Singleton_hignconcur(); } return singleton; } }
然而,上述代码实现可能存在线程A执行到singleton = new Singleton(),线程B则依然执行到(singleton == null)判断时依然为真,此时,由于A尚未返回实例判断条件仍未null,此时可能出现2个对象,因此线程不安全。
解决上述线程不安全的方法一般有2种,(1)在getSingleton()方法前添加synchronized关键字,或在该方法内添加synchronized关键字,也可使用饿汉式单例(例1)或懒汉式单例模式(例2);(2)考虑对象复制的情况。在JAVA中对象默认不能复制,若实现了Cloneable接口,并实现clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制不需要调用类的构造函数,因此,即使是私有的构造函数,对象仍可被复制。因此,尽量使单例类不实现Cloneable接口。
4. 单例模式扩展
一个类需要多个对象时,直接使用new关键字即可,只能有一个对象时,使用单例模式,但对于要求某各类只能有n(n可为任一固定自然数)个对象时,如何实现?
public class Emperor { //定义最多能产生的对象数量 private static int maxNum=3; //每个对象(皇帝)拥有的属性(姓名),使用list存储 private static ArrayList<String> nameList = new ArrayList<String>(); //定义一个列表,容纳所有的对象实例 private static ArrayList<Emperor> emperorList= new ArrayList<Emperor>(); //定义当前皇帝的序号 private static int order=0; //产生所有对象 static { for(int i=0;i<maxNum;i++){ emperorList.add(new Emperor("皇帝"+(i+1))); } } private Emperor(){ //存在约束条件(世俗和道德),使得不产生第二个皇帝 } //传入皇帝名称,建立一个对象 private Emperor(String name){ nameList.add(name); } //随机获得一个皇帝实例 public static Emperor getInstance(){ Random ran=new Random(); order= ran.nextInt(maxNum); return emperorList.get(order); } //方法 public static void say(){ System.out.println(nameList.get(order)); } }
public class Minister { public static void main(String[] args){ //定义5个大臣 int maxNum=5; for(int i=0;i<=maxNum;i++){ Emperor emp=Emperor.getInstance(); System.out.println("第"+(i+1)+"个臣子的是:"); emp.say(); } }
}
运行结果如下:
这种需要产生固定数量对象的模式称作有上限的多例模式,是单例模式的一种扩展。采用多例模式可以在设计时觉得在内存中有多少个实例,方便系统进行扩展,修正可能存在的性能问题,提供系统响应速度。
参考《大话设计模式》与《设计模式之禅》
以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章