单例设计模式不同实现(秀恩爱版)
Java设计模式—单例设计模式(秀恩爱版)
对于系统中的某些类来说,只有一个实例很重要。就像男朋友(类)只有一个ChaoWang(对象)。
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
实现:
①. 私有化类的构造器
②. 提供一个自身的静态私有成员变量
③. 提供公共静态方法,返回当前类的对象。
通常单例模式在Java语言中,有两种构建方式:
- 懒汉式。指全局的单例实例在第一次被使用时构建。
- 饿汉式。指全局的单例实例在类装载时构建,实例在整个程序周期都存在。
单例模式有很多种写法,大部分写法都或多或少有一些不足。下面将分别对这几种写法进行介绍。
1.饿汉式
public class BoyFriend { |
饿汉式的好处是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。但它的缺点也很明显,即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
2.懒汉式
public class BoyFriend { |
懒汉式单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。但缺点是此写法不安全,如果两个线程同时运行到判断ChaoW是否为null的if语句,并且ChaoW的确没有被创建时,那么两个线程都会创建一个实例,此时类型BoyFriend就不再满足单例模式的要求了。
3.懒汉式改进(加锁)
public class BoyFriend { |
但这样每次调用getName()方法时都被synchronized关键字锁住,可能会引起线程阻塞,影响程序的性能。
4.懒汉式改进(双重检验锁)
为了在多线程环境下,不影响程序的性能,不让线程每次调用getName()方法时都加锁,而只是在实例未被创建时再加锁,在加锁处理里面还需要判断一次实例是否已存在。
public class BoyFriend { |
可以看到上面在同步代码块外多了一层ChaoW为空的判断。由于单例对象只需要创建一次,如果后面再次调用getName()只需要直接返回单例对象。因此,大部分情况下,调用getName()都不会执行到同步代码块,从而提高了程序性能。
5.静态内部类
加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
public class BoyFriend { |
这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
6.枚举
public class BoyFriend { |
获取实例的方式很简单,MyLove.LOVE.getName()
单例是如何被保证的:
- 首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法。
- 同时每个枚举实例都是
static final
类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。 - 也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的
LOVE
也被保证实例化一次。
单例模式的线程安全性
单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。总结一下:
(1)饿汉式:线程安全
(2)懒汉式:非线程安全
(3)双检锁:线程安全
(4)静态内部类:线程安全
(5)枚举:线程安全