Singleton模式之多线程

一、引言

自己也算是使用Singleton的老客户了,也明白如何写代码能最大限度的避免多线程产生多个实例。但是自己却很少遇到在多线程下使用Singleton,所以自己也是一知半解。

今天正好有空,搞搞清楚这个问题。

二、问题的产生

请看如下代码:

public class Animal
    {
        private static  Animal instance = null;
        private Animal()
        {
        }
        public static Animal getInstance()
        {
            if (instance == null)
            {
                instance = new Animal();
            }
            return instance;
        }
    }

经常看到有些网友这样写。这样写在大都数情况下挺适用的。我们知道操作系统为每个独立线程安排一些CPU时间。单CPU操作系统以轮转方式向线程提供时间片,每个线程在使用完时间片后交出控制,系统再将CPU时间片分配给下一个线程,如下代码,我将使某个线程睡眠模拟线程的时间片用完。代码如下:

  public static Animal getInstance()
        {
            if (instance == null)
            {
                if (Thread.CurrentThread.Name == "Thread1")
                {
                    Thread.Sleep(2000);//模拟Thread1的时间片用完了
           }
                instance = new Animal();
            }
            return instance;
        }
 private void BeginThread()
        {
            Thread th = new Thread(this.Run){IsBackground=true};
            th.Name = "Thread1";
            th.Start();
            Thread th2 = new Thread(this.Run2) {IsBackground=true };
            th2.Name="Thread2";
            th2.Start();
        }
        private void Run()
        {
           Animal p1 = People.Instance;
        }
        private void Run2()
        {
           Animal p2 = People.Instance;
        }

如上Animal p1与Animal p2就是不同的实例。

当然了,这里只是模拟一下时间片的轮换,虽然是模拟但是却能很好的说明问题,上面的Singleton有问题,那么应该怎么写呢?

我想有2个方法:

方法一:锁机制

public class Animal
    {
        private static  Animal instance = null;
        private static object sync = new object();
        private Animal()
        {
        }
        public static Animal getInstance()
        {
            if (instance != null)
            {
                lock (sync)
                {
                    if (instance == null)
                    {
                        instance = new Animal();
                    }
                }
            }
            return instance;
        }
    }

我们可以通过锁住部分代码,同时间只让一个线程执行。有人要问了:为什么最外面还要用 "if (instance != null)"

因为同步操作执行时间长,也耗资源,在最外面加个判断能够更高效的实现Singleton.

值得注意的是:对于NET的执行机制,编译后再执行,编译会微调我们的代码,既然微调那么很可能执行的逻辑就会改变,为了严格意义上执行如上代码,可以加上volatile关键字

如:private static volatile Animal instance。

方法二:如果使用NET进行开发,可以借助于静态构造器的功能

public sealed class People
    {
        private People()
        {
        }
        public static readonly People Instance;
        static People()
        {
            Instance = new People();
        }
    }

静态构造器自身就可以保证,多线程情况下,系统就可以保证只有一个执行。 使用readonly可以防止使用Instance=null设置实例。

当然了,NET还有其他机制可以避免伪Sigleton,请大家能够给出。

原文地址:https://www.cnblogs.com/smallstone/p/1997666.html