单例模式
定义:一个类只有一个实例,提供一个全局访问点。
应用场景:
操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作。windows的Recycle Bin(回收站)也是典型的单例应用。
网站的计数器,一般也是采用单例模式实现。
应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
单例模式应用的场景一般发现在以下条件下:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
关键点:
a.私有构造函数;
b.私有静态实例;
c.公有静态访问入口;
d.使用lock,满足多线程环境;
e.lock开销。
定义类:
public class Singleton { //私有构造函数,防止外部成员通过new实例化对象。 private Singleton() { } //私有静态变量来存放实例 private static Singleton uniqueSingleton = null; //多线程的情况,防止被多次实例化。 private static readonly Object locker = new object(); //提供一个外部访问点 public static Singleton GetInstance() { if (uniqueSingleton == null) { lock (locker) { uniqueSingleton = new Singleton(); } } Count += 1; return uniqueSingleton; } //统计被调用次数 private static int Count; //测试方法 public string ShowTime() { return "当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"); } public string ShowCount() { return Count.ToString(); }
调用:
Console.WriteLine("单例模式:"); Console.WriteLine(Singleton.GetInstance().ShowTime()); Console.WriteLine("休息2秒..."); Thread.Sleep(2000); Console.WriteLine(Singleton.GetInstance().ShowTime()); //获取访问次数 String ReadCount = Singleton.GetInstance().ShowCount(); Console.WriteLine("该实例一共被访问了:{0}次", ReadCount); Console.ReadLine();
输出:
单例模式分类:
a.饿汉单例类
当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。如果使用饿汉式单例来实现负载均衡器LoadBalancer类的设计,则不会出现创建多个单例对象的情况,可确保单例对象的唯一性。
public class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton getInstance() { return instance; } }
b.懒汉单例类
该懒汉式单例类在getInstance()方法前面增加了关键字synchronized进行线程锁,以处理多个线程同时访问的问题。但是,上述代码虽然解决了线程安全问题,但是每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。
public 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;
}
}
c.IoDH(Java)
Initialization on Demand Holder (IoDH)的技术。在IoDH中,我们在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用。
//Initialization on Demand Holder public class Singleton { private Singleton() { } private static class HolderClass { private final static Singleton instance = new Singleton(); } public static Singleton getInstance() { return HolderClass.instance; } public static void main(String args[]) { Singleton s1, s2; s1 = Singleton.getInstance(); s2 = Singleton.getInstance(); System.out.println(s1==s2); } }
单例模式总结
单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高,在很多应用软件和框架中都得以广泛应用。
1.主要优点
单例模式的主要优点如下:
(1) 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
(2) 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
(3) 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。
2.主要缺点
单例模式的主要缺点如下:
(1) 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
(2) 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
(3) 现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
3.适用场景
在以下情况下可以考虑使用单例模式:
(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。