设计模式(1)-单例模式

单例模式


饿汉式
懒汉式
懒汉双加锁
静态内部类
C#特有写法
破坏单例

  • 定义:有且仅有一个实例,节省资源。
  • 构造函数私有化
  • 静态只读私有字段
  • 静态公开的获取私有字段的方法

饿汉式

public class HungryMan
{
// 构造函数私有化
  private HungryMan() { }

// 静态只读私有字段
  private readonly static HungryMan _HungryMan = new HungryMan();

// 静态获取私有字段的方法,也可以写个属性
  public static HungryMan GetHungryMan() 
  {
  	return _HungryMan;
  }
}
问题:
  • 由于有静态字段,所以程序加载静态字段就加载,即使目前用不到,他也会加载,所以有一定的资源浪费。

懒汉式

public class LazyMan
{
   private LazyMan() { }// 构造函数私有化

   private static LazyMan _LazyMan;// 私有字段
   public static LazyMan GetLazzyMan() // 静态获取私有字段的方法
   {
   	if (_LazyMan==null)
   	{
   		_LazyMan = new LazyMan();
   	}		
   	return _LazyMan;
   }
}
问题
  • 线程不安全,可能同时有多个线程同时进入if中
改造
  • 加一个互斥锁
public class LazyMan
{
	private LazyMan() { }// 构造函数私有化

	private static LazyMan _LazyMan;// 私有字段
	private static object o = new object();
	public static LazyMan GetLazzyMan() // 静态获取私有字段的方法
	{
		lock (o)// 互斥锁,只允许一个线程进入。其实就是Monitor.Enter()  Monitor.Exit()
		{
			if (_LazyMan == null)
			{
				_LazyMan = new LazyMan();
			}

		}
		return _LazyMan;
	}
}
问题
  • 每次都进互斥锁,有额外性能开销
  • _LazyMan = new LazyMan();的正常顺序为:
  • 1.开辟内存空间
  • 2.创建一个对象
  • 3.对象指向内存空间
  • 可能会有极端情况使123乱序为132,即指针重排,所以字段加volatile关键字

改造:饿汉双加锁

public class LazyMan
{
   private LazyMan() { }// 构造函数私有化

   private volatile static LazyMan _LazyMan;// 私有字段,加volatile
   private static object o = new object();
   public static LazyMan GetLazzyMan() // 静态获取私有字段的方法
   {
   	if (_LazyMan == null)
   	{
   		lock (o)// 互斥锁,只允许一个线程进入。其实就是Monitor.Enter()Monitor.Exit()
   		{
   			if (_LazyMan == null)
   			{
   				_LazyMan = new LazyMan();
   			}
   		}
   	}
   	return _LazyMan;
   }
}

静态内部类改造饿汉式

public class HungryStaticMan
{
   private HungryStaticMan() {}
   // 静态内部类
   public static class InnerClass
   {
   	public static HungryStaticMan _HungryStaticMan=new HungryStaticMan();
   }
   // 静态获取方法
   public static HungryStaticMan GetStaticMan()
   {
   	return InnerClass._HungryStaticMan;
   }
}
优点
* 用的时候创建,不会浪费空间

C#提供的懒汉式

public class SampleLazyMan
{
	private SampleLazyMan() {}
	
	private static readonly Lazy<SampleLazyMan> Lazy=new Lazy<SampleLazyMan>(()=>new SampleLazyMan());
	
	public SampleLazyMan GetLazyMan()
	{
		return Lazy.Value;
	}
}
优点
  • 线程安全

破坏单例

用反射,获取单例的构造函数。可以在单例的构造函数中加标志位避免,但标志位同样可以在单例中获取,所以“防君子不防小人”。

posted @ 2023-07-25 21:36  张汉堡  阅读(4)  评论(0编辑  收藏  举报