设计模式--单例模式

  设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

单例模式介绍

单例模式的目的: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

实现

单例模式的实现方式有懒汉式饿汉式

饿汉式

顾名思义,类加载时就初始化,所以也容易产生垃圾对象,浪费内存,基于 classloder 机制避免了多线程的同步问题,也就没有加锁,执行效率会提高。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Single{

//让构造函数为 private,这样该类就不会被实例化
private Single(){}

//创建 SingleObject 的一个对象
private static Single instance = new Single();

//提供了一个静态方法,供外界获取它的静态实例
public static Single getInstance(){
return instance;
}
}

然后别的类通过 Single.getInstance() 获取 Single 类的唯一对象。

懒汉式

线程不安全实现

这种方式属于 lazy loading,因为没有加锁 synchronized,所以不支持多线程。

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {  
private static Singleton instance;
private Singleton (){}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

线程安全实现

通过加 synchronized 保证在多线程中单例,每次调用 getInstance() 方法都需要进行线程锁定判断,加锁会影响效率。

1
2
3
4
5
6
7
8
9
10
public class Singleton {  
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

双重校验锁

利用双重校验锁,第一次检查是否实例已经创建,如果还没创建,再进行同步的方式创建单例对象,安全且在多线程情况下能保持高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
}

静态内部类

类加载的时候并不会实例化 Singleton,而是在第一次调用 getInstance() 加载内部类 SigletonHolder,此时才进行初始化 instance 成员变量,确保内存中的对象唯一性。

1
2
3
4
5
6
7
8
9
public class Singleton {  
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

枚举

枚举的特点是,构造方法是 private 修饰的,并且成员对象实例都是预定义的,因此我们通过枚举来实现单例模式非常的便捷不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化,

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void whateverMethod() {
}
}

总结

实际上,我们应该采用饿汉式还是采用懒汉式,取决于我们希望空间换取时间,还是时间换取空间的抉择问题。一般情况建议使用饿汉式,类被加载时就实例化,因此无须考虑多线程安全问题,并且对象一开始就得以创建,性能方面要优于懒汉式,如果要求懒加载,可以考虑使用静态内部类或者双检锁方式。

-------------本文结束感谢您的阅读-------------
Thank you for your encouragement