欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 设计模式-单例模式

设计模式-单例模式

2024/10/25 5:21:48 来源:https://blog.csdn.net/qq_44845473/article/details/143195379  浏览:    关键词:设计模式-单例模式

一、什么是单例模式

单例模式是一种设计模式,它限制了一个类只能实例化一个对象,并提供一个全局访问点以供其他对象使用

二、为什么要用单例模式

  1. 避免了资源浪费:保证了全局唯一实例,这样可以确保系统的行为一致性,避免了资源的重复创建与浪费。

  2. 访问简单:通过单例模式,其他对象可直接访问,不需要考虑复杂参数传递及上下文切换

  3. 避免了对同一资源的竞争冲突:对于需要共享资源的场景,单例模式可以避免对同一资源的竞争冲突。

  4. 实现了延迟加载:只有在需要使用时才创建实例对象,减少了系统的启动时间和资源占用。

  5. 可维护性强:只有一个实例对象,不需要考虑多个实例之间的一致性问题,只需要关注一个实例对象的状态和行为。

总的来说,单例模式可以提供全局唯一的实例对象,简化了代码的设计和实现,提高了系统的性能和可维护性。但需要注意的是,滥用单例模式可能导致代码的可测试性和可扩展性降低,因此在使用时需谨慎考虑。

三、单例模式的多种实现

1. 懒汉式:

  • 线程不安全:在第一次调用获取实例方法时才创建实例对象。
  • 线程安全:使用同步锁或双重检查锁定(Double-Checked Locking)机制来确保线程安全。

懒汉式实现示例如下:

public class LazySingleton {private static LazySingleton instance;// 私有构造方法,防止外部直接实例化private LazySingleton() {}// 获取唯一实例的方法public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

在懒汉式中,使用了一个静态变量 instance 来保存实例对象,初始时为 null。在 getInstance() 方法中,首先判断 instance 是否为 null,如果是,则创建一个新的实例对象,然后返回。如果 instance 已经存在,则直接返回现有的实例对象。这样可以实现延迟加载,只有在第一次调用 getInstance() 方法时才会创建实例。

需要注意的是,懒汉式的简单实现在多线程环境下是线程不安全的。如果多个线程同时调用 getInstance() 方法,有可能会创建多个实例对象。为了确保线程安全,可以在 getInstance() 方法上加上同步锁,或者使用双重检查锁定(Double-Checked Locking)机制。

2. 饿汉式:

  • 在类加载时就创建实例对象,因此线程安全。

枚举方式实现示例如下:

public class Singleton {// 创建私有的静态实例对象private static Singleton instance = new Singleton();// 私有化构造函数,防止外部直接创建实例对象private Singleton() {// do something}// 提供公共的静态访问方法,返回实例对象public static Singleton getInstance() {return instance;}
}

3. 枚举方式:

  • 枚举类型本身就是单例模式,枚举类型的实例在类加载时就被实例化,因此线程安全。

枚举方式实现示例如下:

public enum Singleton {INSTANCE;// 添加自己需要的属性和方法private int count;private Singleton() {// do something}public int getCount() {return count;}public void setCount(int count) {this.count = count;}// 添加自己需要的其他方法public void incrementCount() {count++;}
}

在上述代码中,Singleton枚举类型只有一个实例 INSTANCE。私有的构造函数保证了其他类不能直接实例化Singleton。可以在枚举类型中添加所需的属性和方法。

使用示例:

public class Main {public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;instance.setCount(10);System.out.println("Count: " + instance.getCount()); // Output: Count: 10Singleton anotherInstance = Singleton.INSTANCE;anotherInstance.incrementCount();System.out.println("Count: " + instance.getCount()); // Output: Count: 11}
}

在上述示例中,我们可以通过Singleton.INSTANCE来访问单例实例。两次获取到的实例对象是相同的,因此对实例的操作是共享的。

枚举方式实现的单例模式具有线程安全性,同时也可以防止反射和反序列化攻击。因此,枚举方式是实现单例模式的推荐方式之一。

4. 双重校验锁(Double-Checked Locking):

  • 使用同步锁和双重检查来确保只有一个实例对象被创建。
  • 单例模式的双重检查锁(Double-Checked Locking)是一种延迟初始化的实现方式,既能保证线程安全性,又能提高性能。

双重检查锁实现示例如下:

public class Singleton {private volatile static Singleton instance;// 添加自己需要的属性和方法private int count;private Singleton() {// do something}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}// 添加自己需要的其他方法public void incrementCount() {count++;}
}

在上述代码中,使用了关键字 volatile 来保证 instance 变量的可见性。在 getInstance() 方法中,首先检查 instance 是否已经被实例化,如果没有,再通过 synchronized 关键字加锁,进行实例化。这样可以避免多个线程同时进入实例化代码块,从而保证线程安全性。使用双重检查锁方式,可以避免每次调用 getInstance() 方法时都进行同步,提高了性能。

使用示例:

public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.setCount(10);System.out.println("Count: " + instance.getCount()); // Output: Count: 10Singleton anotherInstance = Singleton.getInstance();anotherInstance.incrementCount();System.out.println("Count: " + instance.getCount()); // Output: Count: 11}
}

在上述示例中,通过 Singleton.getInstance() 来获取单例实例。两次获取到的实例对象是相同的,因此对实例的操作是共享的。

双重检查锁方式实现的单例模式能够保证线程安全性,并且在实例化时加锁,避免了不必要的同步开销。因此,双重检查锁方式是一种推荐的单例模式实现方式。

5. 静态内部类:

  • 利用类的静态内部类特性,外部类加载时不会加载内部类,只有在第一次使用内部类时才会创建实例,因此具有延迟加载效果。

静态内部类实现示例如下:

public class Singleton {private Singleton() {// do something}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}// 添加自己需要的属性和方法private int count;public int getCount() {return count;}public void setCount(int count) {this.count = count;}// 添加自己需要的其他方法public void incrementCount() {count++;}
}

在上述代码中,私有的静态内部类 SingletonHolder 内部维护了一个私有的静态 final 实例 INSTANCE。在静态内部类中创建单例对象,利用了类加载的特性,保证只有在需要时才会加载静态内部类,从而实现了延迟初始化。而且静态内部类的加载是线程安全的。通过 getInstance() 方法返回单例对象。

使用示例:

public class Main {public static void main(String[] args) {Singleton instance = Singleton.getInstance();instance.setCount(10);System.out.println("Count: " + instance.getCount()); // Output: Count: 10Singleton anotherInstance = Singleton.getInstance();anotherInstance.incrementCount();System.out.println("Count: " + instance.getCount()); // Output: Count: 11}
}

在上述示例中,通过 Singleton.getInstance() 来获取单例实例。两次获取到的实例对象是相同的,因此对实例的操作是共享的。

静态内部类实现的单例模式能够保证线程安全性,并且在需要时才创建单例对象。而且相比双重检查锁方式,静态内部类的方式更加简单、直观。因此,静态内部类方式也是一种常用的单例模式实现方式。

6. 上面5种方式的使用抉择:

这些实现方式各有优缺点,选择合适的方式取决于具体的使用场景和需求。在并发环境下,需要考虑线程安全性。在某些情况下,需要延迟加载实例对象,避免资源浪费。

四、Spring Boot中单例模式的使用方式

1. 使用@Component注解

在需要使用单例对象的类上使用@Component注解来标识,Spring Boot会自动扫描并创建该对象的单一实例。

@Component
public class SingletonService {// ...
}

在其他类中可以通过@Autowired注解将SingletonService注入进来,并且每次注入的都是同一个实例。

2. 使用@Scope注解

使用@Scope注解来指定Bean的作用域为单例模式。

@Component
@Scope("singleton")
public class SingletonService {// ...
}

这样定义的SingletonService就会在整个应用程序中被共享,每次注入或获取该Bean时都会得到同一个实例。

3. 使用@Lazy注解

使用@Lazy注解来延迟加载单例Bean,只有在需要使用时才会创建。

@Component
@Lazy
public class SingletonService {// ...
}

在使用过程中,如果没有主动使用该Bean,Spring Boot不会创建该实例。只有在需要使用时,才会创建并注入。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com