单例模式 是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例的构造函数通常是私有的,这样外部类就无法通过常规方式创建该类的多个实例。通过一个静态方法或者静态变量来获取这个唯一的实例。
一、应用场景
- 资源多次共享访问情况。当系统中某个资源需要被多个不同的部分共享访问,并且只需要一个实例的时候。比如系统的配置信息类,这个系统运行过程中只需要一份配置信息,如果创建多个实例可能会导致配置不一致等问题。
- 在数据库连接池中,因为频繁地创建和销毁数据库连接是非常耗费资源的,所以可以使用单例模式来确保整个应用程序只有一个数据库连接池实例,这样多个线程可以共享这个连接池来获取数据库连接。
- 日志系统。整个应用只需要一个实例来记录日志,方便统一管理和维护日志的输出格式、存储位置等。
- 缓存系统。在内存中维护一个缓存实例来存储经常访问的数据,提高数据访问效率。
二、保证线程安全的三种单例模式
1、饿汉式单例
在类加载的时候就创建实例,因为类加载是线程安全的。
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2、懒汉式单例结合同步方法
在需要获取实例的时候才创建,并且在获取实例的方法上同步关键字,这样可以保证在多线程环境下,只有一个线程能够进入这个方法创建实例。不过这种方式会有性能损耗,因为每次获取实例都要进行同步检查。
class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3、使用双重检查锁定(DCL)来优化懒汉式单例
在同步块外先检查一次实例是否已经创建,这样可以减少进入同步块的次数,提高性能。但这种方式在 Java 5 之前可能会有问题,因为 Java 5 之前的内存模型不能保证指令重排序的正确性。在 Java 5 之后,通过使用 volatile 关键字来修饰实例变量可以解决这个问题。
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;
}
}