剑指Offer——实现单例模式

题目:设计一个类,我们只能生成该类的一个实例。

解法一:只适用于单线程

代码如下:

public sealed class SingleTon1
    {

        public static void Print()
        {
            Console.WriteLine("Singleton1 print");
        }

        private SingleTon1()
        {
            Console.WriteLine("An instance of Singleton1 is created.");
        }
        private static SingleTon1 instance = null;
        public static SingleTon1 Instance
        {
            get
            {
                if (instance == null)
                    instance = new SingleTon1();
                return instance;
            }
        }
    }

输出:Singleton1 print

分析:Print方法用于主函数调用,后面的亦是。声明一个private的构造函数是为了无法在类的外部创建实例,确保只创建一个实例。后面的就是常规写法了。那么,为什么无法用于多线程呢?设想一下,当多个线程同时执行到Instance的if语句时,这时候会创建多个实例,这就不是单例模式了,单例模式是只有一个实例的。

解法二:使用同步锁,使得可在多线程环境中使用

代码如下:

public sealed class Singleton2
    {
        private Singleton2()
        {
            Console.WriteLine("An instance of Singleton2 is created.");
        }

        public static void Print()
        {
            Console.WriteLine("Singleton2 print");
        }
        private static readonly object syncObj = new object();
        private Singleton2 instance = null;
        public Singleton2 Instance
        {
            get
            {
                lock(syncObj)
                {
                    if(instance==null)
                    {
                        instance = new Singleton2();
                    }
                }
                return instance;
            }
        }
    }

输出:Singleton2 print

分析:这个为什么多线程也适用呢?因为我们先创建了一个object实例,使用lock时只有一个syncObject可以进入判断,也就是只有一个线程可以进入判断,当后面的线程进入时已经有了实例,就不会再创建了,这就符合单例模式一个实例的原则。这个解法有什么缺点呢?每次调用Instance属性创建实例时都有加锁的动作,之后又要释放同步锁,很消耗时间。

解法三:使用同步锁前先进行判断

代码如下:

public sealed class Singleton3
    {
        private Singleton3()
        {
            Console.WriteLine("An instance of Singleton3 is created.");
        }

        public static void Print()
        {
            Console.WriteLine("Singleton3 print");
        }
        
        private static readonly object syncObj = new object();
        private static Singleton3 instance = null;
        public static Singleton3 Instance
        {
            get
            {
                if(instance==null)
                {
                    lock(syncObj)
                    {
                        if (instance == null)
                            instance = new Singleton3();
                    }
                }
                return instance;
            }
        }
    }

输出:Singleton3 print

分析:在加锁之前先判断实例是否存在,若已有实例,则不会再加同步锁。为什么加锁之后还要判断实例是否存在呢?同样可以设想多个线程同时到第一个if语句那里的情况。不过这样的代码实现起来比较复杂,容易出错。

解法四:使用静态构造函数

代码如下:

public sealed class Singleton4
    {
        private Singleton4()
        {
            Console.WriteLine("An instance of Singleton4 is created.");
        }

        public static void Print()
        {
            Console.WriteLine("Singleton4 print");
        }
        private static Singleton4 instance = new Singleton4();
        public static Singleton4 Instance
        {
            get
            {
                return instance;
            }
        }
    }

输出:An instance of Singleton4 is created.

          Singleton4 print

分析:静态函数在哪里呢?当有静态成员初始化的时候,编译器会生成一个默认静态构造函数,也就是instance初始化的时候。然后,静态构造函数是只会调用一次的,所以只会有一个实例。不过,这种解法还有一点不完美,就是无论用不用Instance属性,它都会生成一个实例,那么,能不能改进呢?是可以的。为什么会输出An instance of Singleton4 is created.呢?因为在实例化的时候会调用我们声明的那个私有构造函数,因为是在类的内部实例化。

解法五:嵌套类的静态构造函数

代码如下:

public sealed class Singleton5
    {
        private Singleton5()
        {
            Console.WriteLine("An instance of Singleton5 is created.");
        }
        public static void Print()
        {
            Console.WriteLine("Singleton5 print");
        }

        public static Singleton5 Instance
        {
            get
            {
                return Nested.instance;
            }
        }

        class Nested
        {
            static Nested()
            {

            }
            internal static readonly Singleton5 instance = new Singleton5();
        }
    }

输出:Singleton5 print

分析:当使用Instance属性的时候才会创建实例。

Main方法:

class Program
    {
        static void Main(string[] args)
        {
            SingleTon1.Print();
            Singleton2.Print();
            Singleton3.Print();
            Singleton4.Print();             //一创建实例就会调用构造函数
            Singleton5.Print();
            Console.ReadKey();
        }
    }

你可能感兴趣的:(剑指Offer)