单例模式(SingletonPattern)
概念
单例设计模式(Singleton Design Pattern)。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式 。
实现一个单例需要考虑的问题?
- 构造函数需要时private访问权限的,这样才能避免外部通过new创建实例。
- 考虑对象创建时的线程安全问题。
- 考虑延迟加载的问题。
- 考虑性能问题,是否加锁。
实现方式
1.饿汉式
1 | public class HungryType { |
在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。 这个不支持懒加载不一定就不好,提前初始化确实是一种浪费资源的行为,但是如果你的这个单例类初始化时间过长的话,极端一点,还不如系统运行起来的时候就先加载好,这样用户访问响应变快,当然这个比较极端,空间换时间。
2.懒汉式
1 | public class LazyType { |
懒汉式相对饿汉式来说就是支持懒加载,但是这个synchronized关键字,每次去访问这个方法都会锁,导致这个函数并发度较低。我认为不要使用这种方法。
3.双重检测(DCL)
1 | public class DCLType { |
双重检测锁(DCL)解决了懒汉式synchronized关键字每次访问该方法都加锁的问题。提高了性能。这个方式既支持懒加载又提高了并发度。
volatile
这个instance加这个关键字是因为指令重排的问题,防止对象被new出来,赋值给了instance但是还没来得及初始化,就被另一个线程拿去使用了。可加可不加,现在高版本的Java不存在这个问题了。(这个我只是简单的说一下,这个要了解JVM类加载的过程,字节码指令)
4.静态内部类
1 | public class InnerClassType { |
这个静态内部类的方法是简化了DCL方式,但是又拥有DCL的特性。我感觉这个不错!这种实现方法既保证了线程安全,又能做到延迟加载。
如果了解静态内部类特性的话,肯定知道外部类加载静态内部类不会去加载,等你调用getInstance这个方法的时候才会去加载静态内部类 。这就实现了懒加载。
至于线程安全,这个JVM来保证的。
5.枚举
1 | public enum EnumType { |
之所以存在一个枚举类是因为前面的几种严格意义上来说并不安全,虽然加了synchronized关键字,但是你这个只能保证多线程环境下的安全,Java中存在反射。
枚举是绝对安全的,而且简单,要知道为什么就需要去详细了解enum。简单的说一下这个类反编译后是继承java.lang.Enum这个类的。所以说enum可以算是一种class的一种特殊化。
总结
我只是简单的列举了单例的几种实现方式,其中并没有写一些关于业务的逻辑代码,比如你可以像枚举类那个例子中那样,用单例模式实现一个简单的计数器。这些单例模式前三种肯定要十分明白,尤其是第三种(DCL)。但是我都感觉不太好,我感觉静态内部类较好,枚举类实现单例最好。
上述只是说明单例的实现的几种方式,并没有说明为什么要使用单例模式,单例模式的弊端(破坏面向对象的特性等等),替代单例模式的方案,多例模式(限定数量new多个一个类的实例)的实现方式,可以百度查找,这个我目前只是明白一些并不能总结很好,停留知道怎么使用阶段。



