单例模式(Singleton)确保一个类只有一个实例,且提供一个全局访问点。
什么是单例模式
单例模式是一种非常常用的设计模式,它可以确保一个类只有一个实例,并且提供一个全局访问点。在单例模式中,应用程序中的所有代码都可以访问到该实例对象,从而可以确保该对象的状态和行为都是一致的。单例模式通常使用静态变量去存储该类的唯一实例,同时将构造函数的访问限制为私有,这样就可以防止其他代码创建该类的实例。
单例模式的使用场景
-
当需要确保一个类只有一个实例,并且全局代码都可以访问该实例时,可以使用单例模式。例如,数据库连接池、线程池、配置文件等都可以使用单例模式来确保系统中只有一个实例。
-
当需要对实例进行严格控制并限制其访问时,可以使用单例模式。例如,操作系统中的文件系统和窗口管理器都是使用单例模式来确保只有一个实例,以便进行统一的管理和调度。
单例模式的代码示例
Java 的单例模式实现有以下几种方式:
- 饿汉式单例模式:
public class Singleton {// 类初始化时即创建实例private static final Singleton INSTANCE = new Singleton();// 构造函数私有化,禁止外部实例化private Singleton() {}public static Singleton getInstance() {return INSTANCE;}
}
该方式的缺点是当应用启动时就创建对象,可能会占用资源,甚至出现“灾难性”后果。不过由于其线程安全,简单易懂等特点,在一些极简单的场景中,还是能够使用。
- 懒汉式单例模式:
public class Singleton {// 加上 volatile,防止多线程环境下,由于指令重排导致的 INSTANCE 状态异常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 class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
该方式通过静态内部类来实现懒汉式单例模式,既实现了懒加载,又实现了线程安全,并且也不会出现指令重排等问题。
- 枚举单例模式
public enum Singleton {INSTANCE;public void doSomething() {// do something}
}
枚举单例模式是一种非常优雅的单例模式实现,可以保证线程安全,处理序列化等问题。此时,实例就是一个枚举值,每个枚举值都是一个全局唯一的实例。
当然可以,以下是对上面教程的补充和完善:
- 双重检查锁定单例模式
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 class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
该方式在实现原理上和懒汉式非常类似,但是将实例化对象的过程放到了一个私有的内部静态类中,在 Singleton 类加载的时候并不会对 SingletonHolder 进行初始化,只有在第一次调用 getInstance() 方法时,才会导致 SingletonHolder 类被加载,这样才实例化 instance。
单例模式的实际应用
下面是几个常用的框架及其使用单例模式的例子:
- Spring 框架
Spring 框架是一种轻量级的 Java 开发框架,广泛应用于企业级应用开发中。在 Spring 框架中,许多组件都使用了单例模式。例如:
- BeanFactory:一个全局的 IoC 容器,其中存储了系统中所有的 Bean 实例。
- ApplicationContext:一个基于 BeanFactory 的上层接口,提供了更多高级的特性,合适于大多数应用。
- ResourceLoader:用于加载各种资源文件(如配置文件、XML 文件等)的工具类。
- ServletContext:一个全局的 Servlet 上下文,存储了与当前 Web 应用相关的各种信息和资源。
这些组件都使用了单例模式,以确保系统中只有一个实例,并且全局可以访问到该实例。
- Log4j 框架
Log4j 是一种常用的 Java 日志框架,可以帮助开发者对系统进行日志记录。在 Log4j 框架中,Logger 类是一个单例类,它负责接收系统产生的日志信息,并将其输出到文件、数据库等目的地。Log4j 框架设计为单例模式,可确保系统中只有一个 Logger 实例,并且可以全局访问到该实例。
- Hibernate 框架
Hibernate 是一种常用的 Java 数据库映射框架,可以帮助开发者将 Java 对象映射到关系型数据库中。在 Hibernate 框架中,SessionFactory 类是一个单例类,它负责管理所有的 Hibernate Session 实例。SessionFactory 类使用单例模式,可以确保系统中只有一个实例,并且全局可以访问到该实例。
- Java 连接池
Java 连接池是一种常用的数据库连接管理工具,可以帮助开发者实现数据库连接的共享和复用。在 Java 连接池中,使用了单例模式来确保数据库连接池对象只有一个实例,并且可以全局访问。例如,Tomcat 容器中的 JDBC 数据库连接池就是使用单例模式实现的。
关注微信公众号:“小虎哥的技术博客”。我们会定期发布关于Java技术的详尽文章,让您能够深入了解该领域的各种技巧和方法,让我们一起成为更优秀的程序员👩💻👨💻!