购物网站建设行业现状,杭州网络公司项目合作,那些免费网站可以做国外贸易,企业建网站程序通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例#xff08;Instance#xff09; 的情况#xff0c; 比如说我们想做一计数器#xff0c;统计某些接口调用的次数#xff0c;通常我们的数据库连接也是只期望有一个实例。Windows系统的系统任务管理器…通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例Instance 的情况 比如说我们想做一计数器统计某些接口调用的次数通常我们的数据库连接也是只期望有一个实例。Windows系统的系统任务管理器也是始终只有一个如果你打开了windows管理器你再想打开一个那么他还是同一个界面同一个实例 还有比如 做.Net平台的人都知道AppDomain 对象一个系统中也只有一个所有的类库都会加载到AppDomain中去运行。只需要一个实例对象的场景随处可见那么有么有什么好的解决方法来应对呢 有的那就是 单例模式。 一、单例模式定义 单例模式(Singleton Pattern)确保某一个类只有一个实例而且自行实例化并向整个系统提供这个实例这个类称为单例类它提供全局访问的方法。单例模式是一种对象创建型模式。 二、单例模式结构图 Singleton单例在单例类的内部实现只生成一个实例同时它提供一个静态的GetInstance()工厂方法让客户可以访问它的唯一实例为了防止在外部对其实例化将其构造函数设计为私有private在单例类内部定义了一个Singleton类型的静态对象作为外部共享的唯一实例。三、 单例模式典型代码 public class Singleton
{private static Singleton instance;private Singleton(){}public static Singleton GetInstance(){if(instancenull){instancenew Singleton();}return instance;}
} 客户端调用代码 static void Main(string[] args)
{Singleton singleto Singleton.GetInstance();
} 在C#中经常将统一访问点暴露出一个只读的属性供客户端程序使用这样代码就变成了这样 public class Singleton
{private static Singleton instance;private Singleton(){}public static Singleton GetInstance{get{if (instance null){instance new Singleton();}return instance;}}
} 客户端调用 static void Main(string[] args)
{Singleton singleton Singleton.GetInstance;
} 四、单例模式实例 1. 懒汉模式 假如我们要做一个程序计数器一旦程序启动无论多少个客户端调用这个 计数器计数的结果始终都是在前一个的基础上加1那么这个计数器类就可以设计成一个单例模式的类。 public class SingletonCounter
{private static SingletonCounter instance;private static int number0;private SingletonCounter() { }public static SingletonCounter Instance{get{if (instance null) instance new SingletonCounter();number;return instance;}}public int GetCounter(){return number;}
} 客户端调用 static void Main(string[] args)
{//App A call the counter;SingletonCounter singletonA SingletonCounter.Instance;int numberA singletonA.GetCounter();Console.WriteLine(App A call the counter get number was: numberA);//App B call the counter;SingletonCounter singletonB SingletonCounter.Instance;int numberB singletonA.GetCounter();Console.WriteLine(App B call the counter get number was: numberB);Console.ReadKey();
} 输出结果 这个实现是线程不安全的如果有多个线程同时调用并且又恰恰在计数器初始化的瞬间多个线程同时检测到了 instancenull为true情况会怎样呢这就是下面要讨论的 “加锁懒汉模式” 2、加锁懒汉模式 多个线程同时调用并且同时检测到 instance null 为 true的情况那后果就是会出现多个实例了那么就无法保证唯一实例了解决这个问题就是增加一个对象锁来确保在创建的过程中只有一个实例。锁可以确保锁住的代码块是线程独占访问的如果一个线程占有了这个锁其它线程只能等待该线程释放锁以后才能继续访问。 public class SingletonCounter
{private static SingletonCounter instance;private static readonly object locker new object();private static int number 0;private SingletonCounter() { }public static SingletonCounter Instance{get{lock (locker){if (instance null) instance new SingletonCounter();number;return instance;}}}public int GetCounter(){return number;}
} 客户端调用代码 static void Main(string[] args)
{ for (int i 1; i 100; i){var task new Task(() {SingletonCounter singleton SingletonCounter.Instance;int number singleton.GetCounter();Console.WriteLine(App call the counter get number was: number);});task.Start();}Console.ReadKey();
} 输出结果 这种模式是线程安全即使在多线程的情况下仍然可以保持单个实例。那么这种模式会不会有什么问题呢假如系统的访问量非常大并发非常高那么计数器就会是一个性能瓶颈因为对锁会使其它的线程无法访问。在访问量不大并发量不高的系统尚可应付如果高访问量高并发的情况下这样做肯定是不行的那么有什么办法改进呢这就是下面要讨论的“双检查加锁懒汉模式”。 3、双检查加锁懒汉模式 加锁懒汉模式虽然保证了系统的线程安全但是却为系统带来了新能问题主要的性能来自锁带来开销双检查就是解决这个锁带来的问题在锁之前再做一次 instancenull的检查如果返回true就直接返回 单例对象了避开了无谓的锁 我们来看下双检查懒汉模式代码 public class DoubleCheckLockSingletonCounter
{private static DoubleCheckLockSingletonCounter instance;private static readonly object locker new object();private static int number 0;private DoubleCheckLockSingletonCounter() { }public static DoubleCheckLockSingletonCounter Instance{get{if (instance null){lock (locker){if (instance null){instance new DoubleCheckLockSingletonCounter();}}}number;return instance;}}public int GetCounter(){return number;}
} 客户端调用代码和“懒汉加锁模式”相同输出结果也相同。 4、饿汉模式 单例模式除了我们上面讲的三种懒汉模式外还有一种叫“饿汉模式”的实现方式“饿汉模式”直接在Singleton类里实例化了当前类的实例并且保存在一个静态对象中因为是静态对象所以在程序启动的时候就已经实例化好了后面直接使用因此不存在线程安全的问题。 下面是“饿汉模式”的代码实现 public class EagerSingletonCounter
{private static EagerSingletonCounter instance new EagerSingletonCounter();private static int number 0;private EagerSingletonCounter() { }public static EagerSingletonCounter Instance{get{number;return instance;}}public int GetCounter(){return number;}
} 五、单例模式应用场景 单例模式只有一个角色非常简单使用的场景也很明确就是一个类只需要、且只能需要一个实例的时候使用单例模式。 六、扩展 1、”饿汉模式“和”懒汉模式“的比较 ”饿汉模式“在程序启动的时候就已经实例化好了并且一直驻留在系统中客户程序调用非常快因为它是静态变量虽然完美的保证线程的安全但是如果创建对象的过程很复杂要占领系统或者网络的一些昂贵的资源但是在系统中使用的频率又极低甚至系统运行起来后都不会去使用该功能那么这样一来启动之后就一直占领着系统的资源不释放这有些得不偿失。 “懒汉模式“ 恰好解决了”饿汉模式“这种占用资源的问题”懒汉模式”将类的实例化延迟到了运行时在使用时的第一次调用时才创建出来并一直驻留在系统中但是为了解决线程安全问题 使用对象锁也是 影响了系统的性能。这两种模式各有各的好处但是又各有其缺点。 有没有一种折中的方法既可以避免一开始就实例化且一直占领系统资源又没有性能问题的Singleton呢 答案是有的。 2、第三种选择 “饿汉模式“类不能实现延迟加载不管用不用始终占据内存”懒汉式模式“类线程安全控制烦琐而且性能受影响。我们用一种折中的方法来解决这个问题针对主要矛盾 即既可以延时加载又不影响性能。 在Singleton的内部创建一个私有的静态类用于充当单例类的”初始化器“专门用来创建Singleton的实例 public class BestPracticeSingletonCounter
{private static class SingletonInitializer{public static BestPracticeSingletonCounter instance new BestPracticeSingletonCounter();} private static int number 0;private BestPracticeSingletonCounter() { }public static BestPracticeSingletonCounter Instance{get{number;return SingletonInitializer.instance;}}public int GetCounter(){return number;}
}这种模式兼具了”饿汉“和”懒汉“模式的优点有摒弃了其缺点可以说是一个完美的实现。