单例模式在我们日常开发中,是我们最常见到的设计模式了。实现单例模式的方法有很多,有些人虽然会写,但是对为什么这么写,还是一点不明白。
下面我来给大家讲讲单例模式为什么这么写,有什么好处。
实现单例模式呢,一般有以下几个关键点:
(1)私有的构造函数(无法通过new创建实例)
(2)通过静态方法或者枚举提供对象的实例
(3)确保单例对象只有一个(考虑多线程情况下)。
有时候在实现单例模式的时候,除了以上三点有时候我们还要防止反射破坏单例,反序列化的时候也会重新构建对象的实例。多进程的环境下,单例模式会失效,因为不同的进程运行在不同的虚拟机中,内存是不一样的。
下面我们来介绍常见的单例模式的写法
1.饿汉式单例模式
上面这两种都是饿汉式单例的写法,为什么说是饿汉式呢。因为上面两种写法,Manager的实例,都是在Manager这个类被初 始化的时候,就被创建好了。这种写法,在多线程的环境下,也能保证只有一个对象被创建。缺点呢就是如果不使用的话,就会白白创建这个单例对象。造成浪费。
2.懒汉式单例
上面这种单例模式,就是懒汉式的实现方法,我们可以看到在getInstance()方法上我们加上了 synchronized的关键字。
这样我们就可以在多线程的环境下,也只会生成一个对象的实例。但是上面这种写法有个很明显的缺点,就是我们的synchronized实现的是类锁,在我们第一次初始化成功之后,以后每次通过getInstance()方法获取对象的实例时,都要先获取
当前类的类锁,在执行完方法后,还要释放锁,造成了资源的不必要的浪费。synchronized操作是一个重量的操作,我们应当尽量避免这种操作。下面我们在来介绍一种懒汉式单例的写法 ,Double Check Log 实现单例。
DCL实现的单例可以有效的避免我们上面的第一种懒汉式写法所造成的每次调用getInstance()都要去获取类锁,执行完释放锁的操作。第一次初始化之后,我们调用该方法,发现对象已经实例化,直接返回当前已经实例化的对象。不会在走下面的同步操作。volatile关键字起到的作用是禁止指令重排序。避免多线程同时在获取manger对象时,由于指令重排序,导致其他线程在获取manger对象的时候,manger对象虽然不为null,但还没有初始化。
3.静态内部类实例单例模式
静态内部类实现单例的写法,是我比较提倡的写法了。第一在类初始化的时候,我们也不会去实例化对象,只有在调用getInsance()方法的时候我们才会去实例化对象,第二有效避免了加锁造成的资源浪费,而且代码更加简洁。而且在多线程的环境下也能保证只有一个实例。
4.枚举实现单例
写法简单是枚举单例的最大优点。而且枚举实例的创建时线程安全的。
还有就是以上的几种单例写法在反序列化的情况下会重新创建对象。而枚举不会。如果上述几种写法想避免在反序列的时候重新创建对象,那必须加入readResolve()方法。
好了今天的文章就到这里了,至于我们文章开头说的反射破坏单例的情况,我们下次在讨论。谢谢大家的观看!!!