定义
单例模式的定义是:确保一个类只有一个实例,并提供全局访问点。
优点
- 全局唯一性:保证一个类只有一个实例,避免了不必要的内存消耗。
- 全局访问点:提供一个全局访问的接口,方便其他代码使用。
缺点
- 可能引起性能问题:在高并发环境下,多个线程同时请求单例对象可能会引起性能瓶颈。
- 对拓展性不好:单例类只能有一个对象实例。但如果未来改需求了,需要创建两个或多个实例,就需要对代码有比较大的改动。
- 不支持有参构造:单例不支持有参数的构造函数,如果想要传递参数,只能在 getInstance 方法中添加参数,或者定义方法传递参数。
应用场景
- 资源共享:例如配置文件、日志对象等需要共享的资源。
- 线程池、连接池:确保全局只有一个实例,避免资源竞争和浪费。
- 日志对象:避免在多个地方创建不同的日志对象,统一管理。
- 缓存:保证缓存的一致性和有效性。
为什么要使用单例
使用单例模式的主要目的是确保一个进程只有一个实例,避免不必要的资源浪费,同时提供一个全局访问点方便其他代码使用。在需要全局唯一性、全局访问点或者共享资源的情况下,单例模式是一个很好的选择。
作用域与范围
单例模式的作用域是全局的,即在整个应用程序中只有一个实例。这样可以确保各个模块之间共享同一个实例,避免了资源的重复创建和管理。
单例是如何保证唯一的
单例模式保证实例的唯一性是通过以下方式实现的:
- 私有化构造方法:单例类的构造方法被私有化,禁止外部直接创建实例。
- 静态方法返回实例:通过一个静态方法返回单例对象,在该方法内部进行实例的创建和管理。
- 线程安全控制:针对多线程环境,通过加锁等方式来保证实例的唯一性和线程安全性。
单例创建方式
1、饿汉式
ublic class HungrySingletonPattern implements Serializable {
private static final HungrySingletonPattern instance = new HungrySingletonPattern();
private static boolean flag = false;
private HungrySingletonPattern() {
//线程安全
synchronized ( HungrySingletonPattern.class) {
if (flag) {
throw new RuntimeException("单例模式不允许多个实例");
}
flag = true;
}
}
public static HungrySingletonPattern getInstance() {
return instance;
}
//反序列化时,会自动调用该方法
@Serial
public Object readResolve() {
return instance;
}
// //静态代码块创建
//
// static {
// instance = new LazySingletonPattern();
// }
//
// public static LazySingletonPattern getInstance() {
// return instance;
// }
}
2、懒汉式
public class LazySingletonPattern implements Serializable {
private static boolean flag = false;
//1.私有化构造方法
private LazySingletonPattern() {
//线程安全
synchronized ( HungrySingletonPattern.class) {
if (flag) {
throw new RuntimeException("单例模式不允许多个实例");
}
flag = true;
}
}
//2.私有化静态变量
//volatile关键字修饰,防止指令重排序,避免多线程下,对象创建失败
private static volatile LazySingletonPattern instance;
//3.提供公共的静态方法
public static LazySingletonPattern getInstance() {
//1.判断是否为空
if (instance == null) {
synchronized (LazySingletonPattern.class){
if (instance == null) {
//2.创建对象
instance = new LazySingletonPattern();
}
}
}
return instance;
}
//反序列化时,会自动调用该方法
@Serial
public Object readResolve() {
return instance;
}
//静态内部类加载,JVM在加载外部类时,是不会加载内部类的,只有当调用静态内部类中的方法时,才会加载静态内部类,并且创建静态内部类对象,
// 静态内部类加载时,会自动加载外部类,并且创建静态内部类对象
// private static class LazySingletonPatternHolder {
// private static final LazySingletonPattern INSTANCE = new LazySingletonPattern();
// }
//
// public static LazySingletonPattern getInstance() {
// return LazySingletonPatternHolder.INSTANCE;
// }
}
3、枚举式
public enum EnumSingletonPattern {
INSTANCE;
}