技术好文共享:设计模式笔记:单件模式(Singleton)

简介: 技术好文共享:设计模式笔记:单件模式(Singleton)

1. 单件模式简介


1.1 定义


  单件模式(Singleton)定义:要求一个类有且仅有一个实例,并且提供了一个全局的访问点,在同一时刻只能被一个线程所访问。


  单件模式的特点:


  (1)单件类只能有一个实例。


  (2)单件类必须自身创建唯一实例。


  (3)单件类必须给所有其它对象提供唯一实例。


1.2 使用频率


   中高


2、单件模式结构


2.1 结构图


2.2 参与者


  单件模式参与者:


  ? Singleton


    ° 被调用的单件对象;


    ° 在单件模式中,通常由Instance()或GetInstance()方法负责对象的创建,该方法应保证每个需要(单件)对象的客户端均能访问。


3. 单件模式结构实现


3.1 单件模式实现要点


  ? 单件类有一个私有的无参构造函数,防止被其他类实例化。


  ? 单件类不能被继承,使用sealed修饰。


  ? 单件类使用静态的变量保存单实例的引用。


  ? 单件类使用公有静态方法获取单一实例的引用,如果实例为null即创建一个。


3.2 C#代码


(1)非线程安全


  主要实现原理:在不考虑并发的情况下,通过以下2点实现单一实例。


  (a) 静态变量和静态方法在内存中唯一。


  (b) 私有构造函数确保不能通过调用构造函数来生成实例。


  Singleton.cs


using System;


using System.Collections.Generic;


using System.Text;


namespace Libing.DesignPatterns.SingletonPattern.Structural


{


///


/// 单件模式实现方式:由于该实现方式非线程安全,在实际应用中不推荐使用。


///


public sealed class Singleton


{


// 定义一个静态变量来保存类的实例


private static Singleton _instance;


// 私有构造函数,防止通过new实例化对象


private Singleton()


{


}


///


/// 定义公有静态方法,获取实例,并加入判断,保证实例只被创建一次


///


///


public static Singleton Instance()


{


// 使用延迟初始化


// 若类的实例不存在则创建实例,若存在则返回实例


// 注: 非线程安全


if (_instance == null)


{


_instance = new Singleton();


}


return _instance;


}


}


}


  Program.cs


using System;


using System.Collections.Generic;


using System.Linq;


using System.Text;


using Libing.DesignPatterns.SingletonPattern.Structural;


namespace Libing.DesignPatterns.SingletonPattern


{


class Program


{


static void Main(string【】 args)


{


// 创建一个实例s1


Singleton s1 = Singleton.Instance();


// 创建一个实例s2


Singleton s2 = Singleton.Instance();


if (s1 == s2)


{


Console.WriteLine("对象为相同实例");


}


}


}


}


  运行输出:


对象为相同实例


请按任意键继续. . .


注:以上的实现方式适用于单线程环境,在多线程的环境下有可能得到Singleton类的多个实例。假如同时有两个线程去判断(null == _singleton),并且得到的结果为真,那么两个线程都会创建类Singleton的实例,这样就违背了Singleton模式“唯一实例”的原则。


  多线程测试:


using System;


using System.Collections.Generic;


using System.Text;


using System.Threading;


namespace Libing.DesignPatterns.SingletonPattern.Structural


{


///


/// 单件模式实现方式:由于该实现方式非线程安全,在实际应用中不推荐使用。


///


public sealed class Singleton


{


// 定义一个静态变量来保存类的实例


private static Singleton _instance;


// 私有构造函数,防止通过new实例化对象


private Singleton()


{


}


///


/// 定义公有静态方法,获取实例,并加入判断,保证实例只被创建一次


///


///


public static Singleton Instance()


{


// 使用延迟初始化


// 若类的实例不存在则创建实例,若存在则返回实例


// 注: 非线程安全


if (_instance == null)


{


Thread.Sleep(1000); // 模拟线程阻塞


_instance = new Singleton();


}


return _instance;


}


}


}


using System;


using System.Threading;


using Libing.DesignPatterns.SingletonPattern.Structural;


namespace Libing.DesignPatterns.SingletonPattern


{


class Program


{


static void Main(string【】 args)


{


Thread t1 = new Thread(new ThreadStart(Display));


t1.Start();


Thread t2 = new Thread(new ThreadStart(Display));


t2.Start();


}


public static void Display()


{


Singleton s = Singleton.Instance();


Console.WriteLine("Singleton:" + s.GetHashCode());


}


}


}


  运行结果:


Singleton:63835064


Singleton:6044116


(2)简单线程安全


using System;


using System.Collections.Generic;


using System.Text;


namespace Libing.DesignPatterns.SingletonPattern.Structural


{


///


/// 单件模式实现方式:简单线程安全。


///


public sealed class Singleton


{


// 定义一个静态变量来保存类的实例


private static Singleton _instance;


// 定义一个标识确保线程同步


private static readonly object _syncLock = new object();


//代码效果参考:http://www.lyjsj.net.cn/wx/art_23929.html

// 私有构造函数,防止通过new实例化对象

private Singleton()


{


}


///


/// 定义公有静态方法,获取实例,并加入判断,保证实例只被创建一次


///


///


public static Singleton Instance()


{


// 当第一个线程运行到这里时,此时会对_syncLock对象 "加锁",


// 当第二个线程运行该方法时,首先检测到_syncLock对象为"加锁"状态,该线程就会挂起等待第一个线程解锁


// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"


lock (_syncLock)


{


// 使用延迟初始化


// 若类的实例不存在则创建实例,若存在则返回实例


if (_instance == null)


{


_instance = new Singleton();


}


}


return _instance;


}


}


}


  以上方式的实现方式是线程安全的,首先创建了一个静态只读的进程辅助对象,由于lock是确保当一个线程位于代码的临界区时,另一个线程不能进入临界区(同步操作)。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放//代码效果参考:http://www.lyjsj.net.cn/wz/art_23927.html

。从而确保在多线程下不会创建多个对象实例了。

  但这种实现方式要进行同步操作,将影响系统性能的瓶颈和增加了额外的开销。


(3)双重锁定线程安全


  在上面简单线程安全代码中,对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在。


  对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,而不必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能。


  为了改进上面实现方式的缺陷,只需要在lock语句前面加一句(uniqueInstance==null)的判断即可避免锁所增加的额外开销,这种实现方式称为“双重锁定”。


using System;


using System.Collections.Generic;


using System.Text;


namespace Libing.DesignPatterns.SingletonPattern.Structural


{


///


/// 单件模式实现方式:双重锁定线程安全。


///


public sealed class Singleton


{


// 定义一个静态变量来保存类的实例


private static Singleton _instance;


// 定义一个标识确保线程同步


private static readonly object _syncLock = new object();


// 私有构造函数,防止通过new实例化对象


private Singleton()


{


}


//代码效果参考:http://www.lyjsj.net.cn/wz/art_23925.html

///

/// 定义公有静态方法,获取实例,并加入判断,保证实例只被创建一次


///


///


public static Singleton Instance()


{


// 当第一个线程运行到这里时,此时会对_syncLock对象 "加锁",


// 当第二个线程运行该方法时,首先检测到_syncLock对象为"加锁"状态,该线程就会挂起等待第一个线程解锁


// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"


// 双重锁定只需要一句判断即可


if (_instance == null)


{


lock (_syncLock)


{


// 使用延迟初始化


// 若类的实例不存在则创建实例,若存在则返回实例


if (_instance == null)


{


_instance = new Singleton();


}


}


}


return _instance;


}


}


}


  NUnit测试:


using System;


using System.Collections.Generic;


using System.Reflection;


using System.Text;


using Libing.DesignPatterns.SingletonPattern.Structural;


using NUnit.Framework;


namespace Libing.DesignPatterns.SingletonPattern.Tests


{


【TestFixture】


public class SingletonTests


{


【Test】


public void TestCreateSingleton()


{


Singleton s1 = Singleton.Instance();


Singleton s2 = Singleton.Instance();


Assert.AreSame(s1, s2);


}


【Test】


public void TestNoPublicConstructors()


{


Type singleton = typeof(Singleton);


ConstructorInfo【】 ctrs = singleton.GetConstructors();


bool hasPublicConstructor = false;


foreach (ConstructorInfo c in ctrs)


{


if (c.IsPublic)


{


hasPublicConstructor = true;


break;


}


}


Assert.IsFalse(hasPublicConstructor);


}


}


}


  Microsoft.VisualStudio.TestTools.UnitTesting测试:


using System;


using System.Reflection;


using Microsoft.VisualStudio.TestTools.UnitTesting;


using Libing.DesignPatterns.SingletonPattern.Structural;


namespace Libing.DesignPatterns.SingletonPattern.Tests


{


【TestClass】


public class SingletonTests


{


【TestMethod】


public void TestCreateSingleton()


{


Singleton s1 = Singleton.Instance();


Singleton s2 = Singleton.Instance();


Assert.AreSame(s1, s2);


}


【TestMethod】


public void TestNoPublicConstructors()


{


Type singleton = typeof(Singleton);


ConstructorInfo【】 ctrs = singleton.GetConstructors();


bool hasPublicConstructor = false;


foreach (ConstructorInfo c in ctrs)


{


if (c.IsPublic)


{


hasPublicConstructor = true;


break;


}


}


Assert.IsFalse(hasPublicConstructor);


}


}


}


4. 单件模式应用分析


4.1 单件模式使用注意点


  (1)不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。


  (2)不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放。


4.2 单件模式适用情形</

相关文章
|
1月前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
|
1月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
17天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
34 2
|
17天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
21 2
|
23天前
|
设计模式 Java Kotlin
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
41 2
|
23天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
39 2
|
23天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
41 1
|
23天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
36 1
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式