定义
单例模式(Singleton Design Pattern)属于创建型设计模式,理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。单例模式是一种创建型设计模式。
意义
- 保证一个类只有一个实例 —— 控制一个类所拥有的实例的数量,作为单例来说就是一个,代码经过简单的修改,可以是任意数量的实例(多例)。比方说我们想控制某些共享资源的实例数量来保证稳定性,如日志。
- 为该实例提供一个全局访问节点 —— 全局访问节点的意义在于可以在程序的任何地方访问特定对象,而不必担心会被覆盖。
真实世界类比
交响乐团的指挥就是一个单例模式,一个交响乐团只有一个指挥,所有的乐团成员都根据指挥的指示演奏(这个指挥就是全局访问节点)
结构
C#代码
using System;
namespace Singleton
{
class Singleton
{
private Singleton() { }
private static Singleton _instance;
public static Singleton GetInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("单例模式,相同的实例");
}
else
{
Console.WriteLine("单例模式,不同的实例");
}
}
}
}
运行结果如下:
单例模式,相同的实例
应用场景
- 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
- 如果你需要更加严格地控制全局变量, 可以使用单例模式。
模式优点
- 提供了对唯一实例的受控访问
- 可以节约系统资源,提高系统的性能
- 允许可变数目的实例(多例类)
模式缺点
- 扩展困难(缺少抽象层)
- 单例类的职责过重
- 由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失
扩展1:多线程时的单例(C#代码)
using System;
namespace Singleton
{
class Singleton
{
private Singleton() { }
private static Singleton _instance;
private static readonly object _lock = new object();
public static Singleton GetInstance()
{
//在同一时刻,加锁那部分的程序只有一个线程可以访问
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
return _instance;
}
}
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("单例模式,相同的实例");
}
else
{
Console.WriteLine("单例模式,不同的实例");
}
}
}
}
运行结果如下:
单例模式,相同的实例
扩展2:双重锁定的单例(C#代码)
上面的代码每次调用都需要加锁,影响了程序的执行效率,通过双重锁定,可以提升执行效率,代码如下:
using System;
namespace Singleton
{
class Singleton
{
private Singleton() { }
private static Singleton _instance;
private static readonly object _lock = new object();
public static Singleton GetInstance()
{
//先判断下实例是否存在,不存在再加锁;如果存在就直接返回
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("单例模式,相同的实例");
}
else
{
Console.WriteLine("单例模式,不同的实例");
}
}
}
}
运行结果如下:
单例模式,相同的实例
扩展3:饿汉式单例(C#代码)
在自己被加载时就将自己实例化的方式被称为饿汉式单例,前面介绍的在自己被第一次引用时才会将自己实例化的方式称为懒汉式单例,下面代码演示了饿汉式单例:
using System;
namespace Singleton
{
//通过sealed阻止派生
public sealed class Singleton
{
private Singleton() { }
//在第一次引用类的任何成员时创建实例
private static readonly Singleton _instance = new Singleton;
private static readonly object _lock = new object();
public static Singleton GetInstance()
{
return _instance;
}
}
class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("单例模式,相同的实例");
}
else
{
Console.WriteLine("单例模式,不同的实例");
}
}
}
}
运行结果如下:
单例模式,相同的实例
懒汉与饿汉单例比较
- 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长
- 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响