一、概述
单例模式
的定义就是整个程序有且仅有一个实例。该类负责创建自己的对象,同时确保只有一个对象被创建。属于设计模式三大类中的创建型模式
。
特点
- 单例类只有一个实例对象
- 该单例对象必须由单例类自行创建
- 单例类对外提供一个访问该单例的全局访问点
UML图
二、常见实现方式
常见的单例模式实现方式有五种:饿汉式
、懒汉式
、双重锁懒汉式
、静态内部类式
和枚举单例
。下面将列举这几种方式的代码实现。
1.饿汉式
public class SingletonDemo1 {
//线程安全的
//类初始化时,立即加载这个对象
//调用效率高。但是不能延时加载
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1() {
}
//方法没有加同步块,所以它效率高
public static SingletonDemo1 getInstance() {
return instance;
}
}
由于该模式在加载类的时候对象就已经创建了,所以加载类的速度比较慢,但是获取对象的速度比较快,且是线程安全的。
2.懒汉式
public class SingletonDemo2 {
//线程不安全的
private static SingletonDemo2 instance = null;
private SingletonDemo2() {
}
//运行时加载对象
public static SingletonDemo2 getInstance() {
if (instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
由于该模式是在运行时加载对象的,所以加载类比较快,但是对象的获取速度相对较慢,且线程不安全。如果想要线程安全的话可以加上
synchronized
关键字,但是这样会付出惨重的效率代价。3.
3.双重锁懒汉式(Double Check Lock)
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 = new Singleton()
对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile
修饰signleton
实例变量有效,解决该问题。
4.静态内部类式
public class Singleton{
private Singleton(){}
private static class SingletonHoler{
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHoler.INSTANCE;
}
}
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
5.枚举单例(推荐使用)
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
该方式简单可自由序列化;保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);线程安全。唯一的缺点是非懒加载方式。
三、优缺点
优点
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。
缺点
- 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
四、使用
在Spring中创建的Bean实例默认都是单例模式存在的。