从源头开始学习 Java 单例模式:线程安全和性能的双重保障
单例模式(Singleton Pattern)是一种设计模式,它允许创建一个类只有一个实例,并提供全局访问点。在许多情况下,这是一种非常有用的模式,例如,当你想确保只有一个数据库连接,或者当你想限制某个资源的数量时。在本文中,我将详细介绍单例模式,包括它的使用场景、实现方式、优缺点以及一些使用该模式的实例。
1. 单例模式的使用场景
单例模式在以下情况下特别有用:
- 当一个类的实例化过程非常耗费资源时,如数据库连接或者文件读取等。
- 当你需要限制一个类的实例数量,例如,一个类只能有一个实例,或者一个类最多只能有几个实例。
- 当你需要全局访问某个对象时,例如,一个日志记录器或者一个配置对象。
单例模式的实现方式 单例模式有多种实现方式,下面介绍其中几种。
1.1 饿汉式
饿汉式是最简单的一种实现方式,它在类加载时就创建了实例,因此是线程安全的。它的代码实现如下:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
在该实现方式中,instance 是静态变量,它在类加载时就被创建了实例。因此,该实现方式是线程安全的。但是,它有一个缺点,即如果该类没有被使用到,那么它的实例也会被创建出来,浪费了资源。
1.2 懒汉式
懒汉式是一种在第一次使用时才创建实例的实现方式。这种方式在多线程环境下可能存在问题,需要进行同步处理。代码实现如下:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在该实现方式中,instance 是静态变量,它在第一次调用 getInstance() 方法时才被创建。由于该方法被加了 synchronized 关键字,所以它是线程安全的。但是,该实现方式的性能可能较低,因为每次调用 getInstance() 方法时都需要进行同步处理。
1.3 双重检查锁式
双重检查锁式是一种改进的懒汉式实现方式,它只在第一次调用 getInstance() 方法时进行同步处理,从而提高了性能。代码实现如下:
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;
}
}
在该实现方式中,instance 是静态变量,并且加了 volatile 关键字,以确保多线程环境下的可见性和有序性。在 getInstance() 方法中,先进行一次非同步的判断,如果 instance 为 null,就进行同步处理。同步处理后,再次判断 instance 是否为 null,如果是,就创建实例。由于加了 volatile 关键字,保证了多线程环境下的可见性和有序性,从而保证了线程安全和性能。
1.4 静态内部类式
静态内部类式是一种非常优雅的实现方式,它利用了 Java 的语法特性,保证了线程安全和性能。代码实现如下:
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
在该实现方式中,SingletonHolder 是一个静态内部类,它在类加载时并不会被初始化。只有在第一次调用 getInstance() 方法时,它才会被加载,从而创建了实例。由于该实现方式只有在第一次调用 getInstance() 方法时才会创建实例,因此是线程安全的。而且,由于静态内部类只会被加载一次,因此也保证了性能。
2. 单例模式的优缺点
单例模式的优点如下:
- 保证了一个类只有一个实例。
- 提供了一个全局访问点,方便了对该实例的访问和使用。
- 减少了内存开销,避免了频繁的创建和销毁对象。
单例模式的缺点如下:
- 单例模式的扩展性较差,因为它限制了实例数量。
- 单例模式可能会增加代码的复杂性。
- 单例模式对单元测试的支持不好,因为它可能会产生一些副作用。
- 单例模式的使用实例
2.1 下面是一些使用单例模式的实例:
- java.lang.Runtime 类:它是一个单例类,表示 Java 运行时环境。
- java.awt.Desktop 类:它是一个单例类,用于打开文件、打开 URI 等操作。
- java.util.logging.Logger 类:它是一个单例类,用于记录日志。
- java.lang.ClassLoader 类:它是一个单例类,用于加载 Java 类。
3. 结论
单例模式是一种非常有用的设计模式,它允许创建一个类只有一个实例,并提供全局访问点。在本文中,我们介绍了单例模式的使用场景、实现方式、优缺点以及一些使用该模式的实例。在实际应用中,我们需要根据具体的情况选择适合的实现方式,并根据实际需求灵活应用单例模式。
转载自:https://juejin.cn/post/7225790183723384891