序
一万个人眼中有一万个哈姆雷特,一万个程序猿中有一万种对单例模式的理解,以下絮叨絮叨我对单例模式的理解。
引言
民间神抵中,财神是一位掌管财运的神仙,百姓渴望富足的心情在财神信仰中被表现的淋漓尽致。同一朝代只有一位财神,这种情况下百姓们也好信仰,大家烧香,膜拜的时候只要提及财神,每个人都知道指的是哪位神仙。不用在财神前面加上特定的称呼,如文财神比干、武财神赵公明,武财神关公,增幅财神沈万三。这一过程反应到软件设计领域就是一个类只能生成一个对象(财神),所有对象对他的依赖都是相同的(拜财神求富贵),因为只有一对象,大家对它的生平和喜好都非常了解,建立稳固的关系,我们将财神这种神仙职位通过代码方式实现出来。
财神每天都要接受百姓香火、分发财运,百姓每天都要给财神上香,财神只能有一个,也就是一个类只能创建一个对象,大家也都知道对象是通过关键字new完成的(当然也有反射等方式可以实现),但是要怎么控制呢?可以通过构造函数完成,肯定有人会说为什么呢?因为使用new关键字创建对象时都会根据传入的参数调用相应的构造函数,我们将构造函数设置为private私有权限不就可以防止外部创建对象了吗?
Mammon代表财神类,People代表百姓类,下面我们来实现这个逻辑,代码如下。
财神类
public class Mammon {
private static final Mammon mammon = new Mammon();// 初始化一个财神
private Mammon() {
// 道德的底线束缚着你,目的就是不希望出现第二个财神抢香火
}
public static Mammon getMammon() {
return mammon;
}
// 财神显灵了
public static void apparition() {
System.out.println("我就是财神,快来膜拜我吧!");
}
}
通过定义一个私有构造器, 避免别的类new出一个对象,而Mammon自己则可以new出一个对象,其他只能通过getMammon获取这个对象。
百姓类
public class People {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Mammon mammon = Mammon.getMammon();
mammon.apparition();
}
// 看看这五天显灵的是不是同一个财神
}
}
运行结果
百姓天天都要给财神上香求富贵,今天膜拜的财神应该和前几天的一样,嗨,还是昨天显灵的财神,仙风道骨、英姿飒飒,这就是我理解的单例模式。
单例模式的定义
单例模式(Singleton Pattern)定义如下:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。Singleton类简称单例类,通过使用private的构造函数确保了在一个项目中只产生一个实例对象,并且是自行实例化的(在Singleton中使用自己的new Singleton())。
##单例模式的几种实现方式
饿汉式
public class Singleton {
private static final Singleton singleton = new Singleton();
// 限制产生多个实例
private Singleton() {}
// 通过该方法获取实例
public static Singleton getSingleton() {
return singleton;
}
// 类中的其他方法,尽量是static
public static void demo() {
System.out.println("hello world! my name is Singleton");
}
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
懒汉式
public class Singleton {
private static Singleton singleton ;
// 限制产生多个实例
private Singleton (){}
// 通过该方法获取实例
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
双检锁/双重校验锁(DCL,即 double-checked locking)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getSingleton() 的性能对应用程序很关键。
单例的应用场景
Spring 在Spring中,每个Bean的默认就是单例的,这样做的优点是Spring容器可以管理这些Bean的声明周期,决定什么时候创建出来,什么时候销毁,销毁时要如何处理等等。
代码地址
https://github.com/dxf1998/MyDesignPattern