单例模式

前言

单例模式有二种,饿汉模式和懒汉模式。这里将去实现单例模式的这些例子,然后分析它们的优缺点和使用场景。

1.懒汉模式(单线程)

    public class SingleThread_Singleton
    {
        private static SingleThread_Singleton _instance = null;
        private SingleThread_Singleton() { }
        public static SingleThread_Singleton instance {
            get {
                if (_instance == null)
                    _instance = new SingleThread_Singleton();
                return _instance;
            }
        }

        public void Show()
        {
            Console.WriteLine("懒汉单线程的单例");
        }
    }

使用到类实例时才会去及时创建,如果代码没有使用多线程去访问类实例,以上写法就可以了,不然的话就需要加线程锁,当多线程访问此对象时,其他线程就需要等待挂起,下面就是多线程的懒汉模式。

2.懒汉模式(多线程)

    public class MultiThread_Singleton
    {
        //变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了
        private static volatile MultiThread_Singleton _instance = null;
        private static readonly object lockHelper = new object();
        private MultiThread_Singleton() { }
        public static MultiThread_Singleton instance {
            get {
                if (_instance == null)
                {
                    lock (lockHelper)
                    {
                        if (_instance == null)
                            _instance = new MultiThread_Singleton();
                    }
                }
                return _instance;
            }
        }
        public void Show()
        {
            Console.WriteLine("懒汉多线程的单例");
        }
    }

大家可能看到这里有几个细节问题。1.为什么需要判断2次实例等于null?2.volatile关键字的作用?3.lock的一些注意事项?

1.这其实叫做双重锁定。外面的_instance == null判断如果去掉,会发生什么事情呢?这个就是所谓的不讲道理,不管什么情况先让多线程进行同步,性能消耗比较大,所以外部的判断是必要的。里面的_instance == null判断如果去掉,会发生什么事情呢?如果多线程都通过了外层的判断进行排队,没有内部的判断,那将会实例化多个对象出来,所以这里还需要进行一次判断,保证线程的安全。

2.volatile 关键字指示一个字段可以由多同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。也就是说cpu不能进行缓存,每次访问时都要去内存里获取最新的值。

3.lock锁引用类型的对象,string类型除外。lock推荐的做法是使用静态的、只读的、私有的对象。保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。lock(this)可能会造成死锁,也就是无效的线程锁,因为不同的类对象会导致this不同。所以最好自己定义一个静态的、只读的、私有的对象去锁定。

3.饿汉模式

    public class Static_Singleton
    {
        public static readonly Static_Singleton instance = new Static_Singleton();
        private Static_Singleton() { }
        public void Show()
        {
            Console.WriteLine("饿汉模式的单例");
        }
    }

这个类的实例启动的时候就进行初始化了,专治各种花里胡哨。我们可以在public static readonly Static_Singleton instance = new Static_Singleton();打个断点观察一下,发现还没有执行Main函数的时候,就先执行赋值了。所以我们得出在类加载时就完成了初始化,所以程序启动比较慢,但获取对象的速度快,线程安全。

总结

饿汉和懒汉的同异:

同:任何时刻只有一个类实例。

异:饿汉程序启动比较慢,但获取对象的速度快,线程安全。饿汉程序启动比较快,要它的时候,才会进行初始化,所以需要注意多线程死锁的问题。

单例模式应用的场景:

(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。

(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值