剑指Offer学习总结-实现Singleton模式

剑指Offer学习总结-实现Singleton模式

本系列为剑指Offer学习总结,主要是代码案例的分析和实现:
书籍链接:http://product.dangdang.com/24242724.html
原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/


实现Singleton模式

题目

题目: 设计一个类, 我们只能生成该类的一个实例。
只能生成一个实例的类是实现了 Singleton (单例) 模式的类型,
单例设计模式在面向对象程序设计中起着举足较重的作用。

不好的解法一:只适用于单线程环境

单例实现的基本思路:
私有化构造函数(禁止他人创建实例)
定义一个私有的静态(类名直接访问)变量用于存储单例类对象
提供给外界访问这个对象的方法(或者属性)

public sealed class Singleton1
{
    //私有化构造函数
    private Singleton1()
    {
    }
    //私有静态类对象实例
    private static Singleton1 instance = null;
    //外界访问的属性
    public static Singleton1 Instance
    {
        get
        {
            if (instance == null)
                instance = new Singleton1();
            return instance;
        }
    }
}

不好的解法二:多单线程可工作,但效率不高

解法一中的代码在单线程的时候工作正常, 但在多线程的情况下就有问题了.
设想如果两个线程同时运行到 if (instance == null),并且 instance 的确没有创建时,
那么两个线程都会创建一个实例, 此时类型Singleton1 就不再满足单例模式的要求了 ,
为了保证在多线程环境下我们还是只能得到类型的一个实例, 需要加上一个同步锁。

 public sealed class Singleton2
    {
        private Singleton2()
        {
        }
        //线程锁
        private static readonly object syncObj = new object();

        private static Singleton2 instance = null;
        public static Singleton2 Instance
        {
            get
            {
                //加锁
                lock (syncObj)
                {
                    if (instance == null)
                        instance = new Singleton2();
                }

                return instance;
            }
        }
    }

我们还是假设有两个线程同时想创建一个实例。 由于在一个时刻只有一个线程能得到同步锁,
当第一个线程加上锁时, 第二个线程只能等待。当第一个线程发现实例还没有创建时, 它创建出一个实例。 接着第一个线程释放同步锁, 此时第二个线程可以加上同步锁, 并运行接下来的代码。
这个时候由于实例己经被第一个线程创建出来了, 第二个线程就不会重复创建实例了, 这样就保证了我们在多线程环境中也只能得到一个实例。

但是解法二还不是很完美, 我们每次通过属性 Instance 得到SingUton2 的实例, 都会试图加上一个同歩锁, 而加锁是–个非常耗时的操作, 在没有必要的时候我们应该尽量避免。

可行的解法三:加同步锁前后两次判断实例是否已存在

public sealed class Singleton3
    {
        private Singleton3()
        {
        }

        private static 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 中只有当 instance 为 null 即没有创建时, 需要加锁操作 a 当
instance 已经创建出来之后, 则无须加锁。 因为只在第一次的时候 instance
为 null, 因此只在第一次试图创建实例的时候需要加锁
Singleton3 用加锁机制来确保在多线程环境下只创建一个实例, 并用
两个 if 判断来提高效率。 这样的代码实现起来比较复杂, 容易出错, 我们
还有更加优秀的解法

强烈推荐的解法一:利用静态构造函数

C#的语法中有一个函数能够确保只调用一次, 那就是静态构造函数

 public sealed class Singleton4
    {
        //静态构造函数  原文书籍这里是private,我个人认为原书这里是错误的 
        static Singleton4()
        {
        }

        public static void Print()
        {
            Console.WriteLine("Singleton4 Print");
        }
        //创建的时候直接实例化 
        private static Singleton4 instance = new Singleton4();
        //属性直接返回即可
        public static Singleton4 Instance
        {
            get
            {
                return instance;
            }
        }
    }

C#中调用静态构造函数的时机不是由程序员掌控的, 而是当.NET 运行时发现第一次使用一个类型的时候
自动调用该类型的静态构造函数。
因此在 Singleton4 中, 实例 instance 并不是第一次调用属性 Singleton4,Instance的时候创建,
而是在第一次用到 Singlet(m4 的时候就会被创建。
假设我们在Singleton4 中添加一个静态方法,调用该静态方法是不需要创建一个实例的,
Singleton4 方式实现的单例模式,则仍然会过早地创建实例,从时降低内存的使用效率。

强烈推荐的解法一:实现按需创建实例

   public sealed class Singleton5
    {
        Singleton5()
        {
        }

        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();
        }
    }

我们在内部定义了一个私有类型 Nested,当第一次用到这个嵌套类型的时候,
会调用静态构造函数创建 Singleton5的实例 instance。
类型 Nested 只在属性 SingietonS.Instance 中被用到,
由于其私有属性他人无法使用 Nested 类 ,因 此 当 我 们 第 一 次 试 图 通 过 属 性
Singleton5.Instance 得到 Singieton5的实例时, 会自动调用 Nested 的静态构造函数
创建实例Instance。 如果我们不调用属性 Singleton5.Instance
那么就不会触发.NET 运行时调用 Nested, 也不会创建实例, 这样就真正做到了按需创建

关于更多的设计模式方面,可以查看我的设计模式系列的博客总结。
http://blog.csdn.net/wwlcsdn000/article/details/78678975

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值