likes
comments
collection
share

从源头开始学习 Java 单例模式:线程安全和性能的双重保障

作者站长头像
站长
· 阅读数 4

单例模式(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. 单例模式的优缺点

单例模式的优点如下:

  1. 保证了一个类只有一个实例。
  2. 提供了一个全局访问点,方便了对该实例的访问和使用。
  3. 减少了内存开销,避免了频繁的创建和销毁对象。

单例模式的缺点如下:

  1. 单例模式的扩展性较差,因为它限制了实例数量。
  2. 单例模式可能会增加代码的复杂性。
  3. 单例模式对单元测试的支持不好,因为它可能会产生一些副作用。
  4. 单例模式的使用实例

2.1 下面是一些使用单例模式的实例:

  1. java.lang.Runtime 类:它是一个单例类,表示 Java 运行时环境。
  2. java.awt.Desktop 类:它是一个单例类,用于打开文件、打开 URI 等操作。
  3. java.util.logging.Logger 类:它是一个单例类,用于记录日志。
  4. java.lang.ClassLoader 类:它是一个单例类,用于加载 Java 类。

3. 结论

单例模式是一种非常有用的设计模式,它允许创建一个类只有一个实例,并提供全局访问点。在本文中,我们介绍了单例模式的使用场景、实现方式、优缺点以及一些使用该模式的实例。在实际应用中,我们需要根据具体的情况选择适合的实现方式,并根据实际需求灵活应用单例模式。