- 携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
何为单例?
单例模式是一种创建型设计模式,是一种相对比较简单的设计模式之一,在我们平时的编程中也非常的常见。它能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
应用场景
- 如计算机系统操作同一个文件,必须是一个实例。
- 任务管理器,无论打开多次,它都只有一个窗口
- 打印机任务,都只能执行一个任务等等
如何使用单例
单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。这样就减少了全局类重复的实例化使用。
- 如下代码,创建一个ShowDialog类。注意:所有类都有构造方法,如果不指定会生成一个默认的构造方法。 要实现单例就要阻止new,那么如何阻止呢?简单粗暴直接修改构造函数为私有private
public class ShowDialog { }
// 等同于
public class ShowDialog
{
public ShowDialog() { }
}
var dialog = new ShowDialog();// 编译不会出现任何问题,可以正常new
// 带有私有构造函数
public class ShowDialog
{
private ShowDialog() { }
}
new ShowDialog(); // 编译会出错,无法访问,具有保护级别。
- 单例模式实现之饿汉式:构造函数改为私有后,那我们如何实例化?我们可以创建一个publish方法,为外界提供获取实例。如下实现方式也称之为饿汉式,即类在初始化的时候,无论是否调用GetInstance 实例都会被初始化,浪费内存。
public static void Button_Click()
{
var dialog1 = ShowDialog.GetInstance();
var dialog2 = ShowDialog.GetInstance();
if (dialog1 == dialog2)
{
Console.WriteLine("是同一个实例!");
}
}
public class ShowDialog
{
private static readonly ShowDialog showDialog = new ShowDialog();
private ShowDialog() { } // 私有构造函数
// 获取该类实例的访问点
public static ShowDialog GetInstance()
{
return showDialog;
}
}
- 懒汉式,线程不安全。在多线程模式下,多次调用GetInstance可能会出现多个ShowDialog实例。
public class ShowDialog
{
private static ShowDialog showDialog;
private ShowDialog() { }
public static ShowDialog GetInstance()
{
if(showDialog == null) //不存在则创建一个实例
showDialog = new ShowDialog();
return showDialog;
}
}
- 多线程下的单例模式:加锁、双重锁定!具体代码实现如下
public class ShowDialog
{
private static ShowDialog showDialog;
private static object _lock = new object();
private ShowDialog() { }
public static ShowDialog GetInstance()
{
if (showDialog == null)
lock (_lock)
{
if (showDialog == null)
{
showDialog = new ShowDialog();
}
}
return showDialog;
}
}
Lock: 在多线程中,使用lock能够让线程按顺序执行,如果块有线程占用,其他线程只能等待!仔细看代码会发现上面有两处
showDialog == null判断,第一层判断,如果没有产生实例则走下一步,在多线程中第一个线程进去创建之后出来,第二个线程进去,如果没有第二层判断,那么第二个线程还是会创建一个实例,就没有达到单例效果!
总结
单例模式非常简单,但是用好它也尤为重要。使用单例能减少频繁的对象创建、销毁,减少内存开销,单例模式会阻止实例化,能保证所有对象访问的都是唯一实例,但是在多线程模式下,也需要进行特殊处理,避免重复创建对象。