确保一个类在整个应用程序的生命周期中只有一个实例,并提供一个全局访问点来访问该实例。
根据单例模式的定义,单例模式主要具有以下特点:
1.一个类需要经常被创建或销毁时,可以使用单例模式减少资源浪费。
2.单例模式可以控制创建实例的数量。
3.单例模式创建的对象可以与其他对象共享状态,确保其他类依赖同一个单例实例
4.可以避免对资源的多重占用。
1.生产唯一序列号。
2.管理web应用的计数器,避免每次请求都重新计算。
3.对于资源消耗较大的类,例如频繁执行I/O操作的类。
1.创建一个类,并将其构造函数设置为私有的不可调用的。
2.为该类创建一个静态的“getInstance”全局访问方法,来获取该类的实例引用。
3.在getInstance方法中如果单例对象已经存在,则直接返回,如果不存在,则调用实例化方法创建一个新的对象并返回。
4.如果单例对象尚未创建,则在getInstance 方法中使用同步锁‘synchronized’来保护单例对象的实例化过程。
1.懒汉式 - 线程不安全
这种方式的特点是实现简单,可以实现对象懒加载,但这种方式最大的缺点是线程不安全,在多线程情况下不能正常工作。
实现代码如下:
/*<懒汉式-线程不安全>实现代码*/
public class Singleton {
//1.私有化构造函数,
// 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
private Singleton() {}
//保存全局唯一单例对象
private static Singleton singleton;
//3.通过全局静态访问方法向其他对象提供单例对象
public static Singleton getInstance() {
//2.自行实例化单例对象
if(singleton == null) {// 确保只有一个单例对象(线程不安全)
singleton = new Singleton();
}
return singleton;
}
}
2.懒汉式 - 线程安全
这种方式的特点是实现简单,能实现对象懒加载,同时兼顾线程安全,缺点是使用synchronized 同步锁,导致频繁获取单例对象时效率低下。
实现代码如下:
/*<懒汉式-线程安全>实现代码*/
public class Singleton {
//1.私有化构造函数,
// 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
private Singleton() {}
//保存全局唯一单例对象
private static Singleton singleton;
//3.通过全局静态访问方法像其他对象提供单例对象
public static synchronized Singleton getInstance() {
//2.自行实例化单例对象
if(singleton == null) {// 确保只有一个单例对象(线程不安全)
singleton = new Singleton();
}
return singleton;
}
}
3.饿汉式
这种方式的特点是在类加载时就初始化单例对象,同时基于classloader 类加载机制避免了多线程问题,由于没有加锁,效率很高;缺点是对象没有实现懒加载,容易产生垃圾对象。
实现代码如下:
/*<饿汉式>实现代码*/
public class Singleton {
//1.私有化构造函数,
// 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
private Singleton() {}
//2.自行实例化单例对象, 并保存全局唯一单例对象
private static Singleton singleton = new Singleton();
//3.通过全局静态访问方法像其他对象提供单例对象
public static synchronized Singleton getInstance() {
return singleton;
}
}
4.双重锁/双重校验锁
这种方式的特点是通过双重锁实现线程安全并保证高效率,缺点是实现代码略显复杂。
实现代码如下:
/*<双重锁/双重校验锁>实现代码*/
public class Singleton {
//1.私有化构造函数,
// 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
private Singleton() {}
//保存全局唯一单例对象
private volatile static Singleton singleton;
//3.通过全局静态访问方法像其他对象提供单例对象
public static Singleton getInstance() {
//2.自行实例化单例对象,并通过双重锁或双重校验锁保证线程安全
if(singleton == null) {
synchronized(Singleton.class) { //双重锁
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5.登记式/静态内部类
这种方式既通过classloader 类加载机制保证线程安全,有实现了实例对象的懒加载(只有静态内部类实际被使用时才会加载静态内部类,并生成实例对象)。
实现代码如下:
/*<登记式/静态内部类>实现代码*/
public class Singleton {
//1.私有化构造函数,
// 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
private Singleton() {}
private static class SingletonHolder {
//2.自行实例化单例对象,保存全局唯一单例对象
private static final Singleton INSTANCE = new Singleton();
}
//3.通过全局静态访问方法像其他对象提供单例对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6.枚举
这种方式特点是线程安全,支持序列化机制,可以避免反序列化创建新对象,绝对防止多次实例化。是实现单例模式的最佳方式
实现代码如下:
/*<枚举>实现代码*/
public enum Singleton {
INSTANCE;
public void getConfig() {}
}
总结:
一般情况下不建议使用懒汉式实现单例模式;
如果实例化时消耗资源比较少,建议实用饿汉式实现单例模式;
如果实例化时消耗资源多且明确需要懒加载单例对象时,才建议实用“登记式/静态内部类”实现单例模式;
如果要防止反序列化创建对象时,可以使用枚举实现单例模式;
双重锁/双重校验锁实现比较复杂,不建议使用。
1.Spring 框架中每个Bean默认都是通过单例进行管理的,每个Bean在Spring IOC 容器中只有一个实例。
2. Spring框架中的ApplicationContext代表了Spring IOC 容器,负责管理Bean的生命周期,它本身就是一个单例,确保整个应用程序中只有一个实例。
单例模式由于没有接口,不能继承,因此不符合单一职责原则,一个类应该只关注它的内部逻辑,而不应该关注外部类怎么实例化它。