1、单例模式(重点)
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。这对于需要控制资源访问、限制实例数量或跟踪对象状态的情况很有用。
优点:
- 单一实例:单例模式确保一个类只有一个实例,并提供全局访问点来获取该实例。
- 全局访问:通过单例模式,可以在应用程序的任何位置访问该类的唯一实例。
- 延迟实例化:单例模式可以延迟类的实例化,直到第一次访问该实例时才进行初始化。
- 线程安全:单例模式可以提供线程安全的访问方式,确保多线程环境下只有一个实例被创建。
- 全局资源管理:适用于管理全局资源,例如数据库连接池、日志记录器等。
缺点:
- 可能引起性能问题: 在多线程环境下,某些单例模式的实现可能引起性能问题,如使用锁机制来保证线程安全。
- 扩展困难: 单例模式一般是在创建对象时初始化,不容易扩展新的实例。
- 违反单一职责原则: 由于单例模式中一般负责对象的创建和管理,可能导致类职责过重。
- 对测试不友好: 单例模式可能对测试造成困扰,因为单例对象可能在整个应用生命周期中存在,难以在测试中替换为模拟对象。
常见的家中实现单例模式方式
在Java中,有几种实现单例模式的方式,其中比较常见的有以下几种:
懒汉模式就是使用的时候创建对象,饿汉模式就是提前加载创建好的静态static对象,双重检查模式就是两次检查避免多线程造成创建了多个对象。
懒汉式(Lazy Initialization):
在第一次访问时创建实例,如果实例已经存在,则直接返回。线程不安全,如果没有线程安全的处理方式,可能会导致多个线程同时创建实例的问题。(非线程安全)
代码语言:java复制public class LazySingleton {
// 私有静态变量,用于保存唯一实例
private static LazySingleton instance;
// 私有构造方法,防止外部直接实例化
private LazySingleton() {}
// 公共静态方法,提供全局访问点
public static synchronized LazySingleton getInstance() {
// 如果实例尚未创建,则在同步块内创建实例
if (instance == null) {
instance = new LazySingleton();
}
// 返回实例
return instance;
}
}
饿汉式(Eager Initialization):
在类加载时就创建实例,确保了线程安全,但可能导致资源浪费,因为实例在应用程序启动时就被创建,即使后续不使用。(线程安全)
代码语言:java复制public class EagerSingleton {
// 在类加载时就创建实例
private static final EagerSingleton instance = new EagerSingleton();
// 私有构造方法,防止外部直接实例化
private EagerSingleton() {}
// 提供全局访问点
public static EagerSingleton getInstance() {
return instance;
}
}
双重检查锁(Double-Checked Locking):
在懒汉式的基础上,加入了双重检查,减少了加锁的频率,提高了性能。需要使用volatile关键字来确保可见性(线程安全)
代码语言:java复制public class DoubleCheckedSingleton {
// volatile关键字确保当instance变量被修改时,所有线程都能够立即看到修改。
private static volatile DoubleCheckedSingleton instance;
// 私有构造方法,防止外部直接实例化
private DoubleCheckedSingleton() {}
// 提供一个全局访问点,使用双重检查来确保在多线程环境下只创建一个实例
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
这只是单例模式的一些实现方式,每种方式都有其优缺点,选择适合项目需求的方式很重要。此外,在Java中,如果使用静态内部类、枚举等方式也能实现单例模式。
案例说明
下面是一个简单的 Java 单例模式的例子,采用懒汉式实现,使用了双重检查锁定来确保线程安全和性能:
代码语言:java复制public class Singleton {
private static volatile Singleton instance;
// 私有构造方法,防止外部实例化
private Singleton() {}
// 获取单例实例的方法
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 其他业务逻辑方法
public void doSomething() {
System.out.println("Singleton is doing something.");
}
public static void main(String[] args) {
// 获取单例实例
Singleton singleton1 = Singleton.getInstance();
// 调用业务逻辑方法
singleton1.doSomething();
// 尝试创建第二个实例,但由于是单例模式,将得到相同的实例
Singleton singleton2 = Singleton.getInstance();
// 输出 true,表示两个引用指向同一个对象
System.out.println(singleton1 == singleton2);
}
}
在上述代码中:
- private static volatile Singleton instance;:使用 volatile 关键字确保在多线程环境下的可见性。
- private Singleton() {}:私有构造方法防止外部实例化。
- public static Singleton getInstance():获取单例实例的方法,使用双重检查锁定保证线程安全,同时在实例为 null 时才创建实例。
- singleton1 == singleton2:输出 true,表示两个引用指向同一个对象,确保单例的唯一性。
使用场景
单例模式适用于以下情况:
- 资源共享: 当系统中有些资源如配置文件、数据库连接池等需要在整个应用中只有一个实例时,可以使用单例模式来确保唯一性。
- 控制实例数量: 在某些情况下,系统中只允许存在一个实例,如线程池、缓存、日志对象等。
- 节省资源: 如果某个对象对系统资源消耗较大,通过单例模式可以避免创建过多实例,从而节省系统资源。
- 全局访问点: 适用于需要在整个系统中提供一个访问点来控制某种资源或服务的情况。
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!