在这篇文章中,我们将探索在 Java 中实现线程安全单例的几种方法,包括热切初始化、双重检查锁定 和 内部静态类 方法。我们还将讨论为什么 Final 关键字有利于确保单例的完整性。
当您在整个应用程序中恰好需要类的一个实例时,单例非常有用。常见用例包括管理共享资源,例如日志记录、配置或连接池。单例确保访问一个类的多个请求共享同一个实例,而不是创建新实例。
急切初始化模式在类加载时创建单例实例。这很简单并且确保了线程安全,因为实例是在 JVM 加载类时创建的。
public final class Singleton { // Instance is created at class loading time private static final Singleton INSTANCE = new Singleton(); // Private constructor prevents instantiation from other classes private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
优点:
缺点:
何时使用它:当单例类是轻量级的并且您确定它将在应用程序运行时使用它时,热切初始化是最好的。
如果您想要延迟创建单例直到需要时(称为延迟初始化),双重检查锁定提供了一个线程安全的解决方案。它使用最少的同步并确保仅在第一次访问实例时才创建实例。
public final class Singleton { // Marked as final to prevent subclassing // volatile ensures visibility and prevents instruction reordering private static volatile Singleton instance; // Private constructor prevents instantiation from other classes private Singleton() {} public static Singleton getInstance() { if (instance == null) { // First check (no locking) synchronized (Singleton.class) { // Locking if (instance == null) { // Second check (with locking) instance = new Singleton(); } } } return instance; } }
第一次检查:同步块之外的 if (instance == null) 检查允许我们避免每次调用 getInstance() 时锁定。这通过绕过初始化后未来调用的同步块来提高性能。
同步块:一旦实例为空,进入同步块可确保只有一个线程创建 Singleton 实例。到达这一点的其他线程必须等待,以防止竞争条件。
第二次检查:在同步块内,我们再次检查实例以确保当前线程等待时没有其他线程初始化它。这一双重检查可确保仅创建一个 Singleton 实例。
可变关键字在双重检查锁定模式中至关重要,以防止指令重新排序。如果没有它,语句instance = new Singleton();在完全初始化之前,其他线程可能会显得已完成,从而导致返回部分构造的实例。易失性保证一旦实例非空,它就被完全构造并且对所有线程可见。
这里使用final关键字来防止子类化。将 Singleton 类标记为 Final 有两个主要好处:
防止子类化:通过将类设为最终类,我们可以防止其他类扩展它。这确保了 Singleton 类只能存在一个实例,因为子类化可能会导致额外的实例,从而破坏单例模式。
信号不变性:final 向其他开发人员明确指示单例类是不可变的,不应扩展。这使得代码更容易理解和维护。
简而言之,final 增强了单例的完整性,并有助于避免子类化带来的意外行为。
优点:
缺点:
何时使用它:当单例类是资源密集型且可能并不总是需要时,或者当需要考虑多线程环境中的性能时,此模式非常有用。
延迟初始化的另一种线程安全方法是内部静态类模式。这利用了 Java 的类加载机制,仅在需要时才初始化单例实例,无需显式同步。
public final class Singleton { // Instance is created at class loading time private static final Singleton INSTANCE = new Singleton(); // Private constructor prevents instantiation from other classes private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
在此模式中,SingletonHelper 类仅在第一次调用 getInstance() 时加载。这会触发 INSTANCE 的初始化,确保延迟加载而不需要同步块。
优点:
缺点:
何时使用它:当您希望使用干净、可维护的代码进行延迟初始化时,请使用内部静态类模式。由于简单性和线程安全性,这通常是首选。
我们研究了在 Java 中实现线程安全单例的三种流行方法:
每种方法都有其优点并适合不同的场景。在您自己的项目中尝试一下,看看哪一个最适合您!如果您有首选方法或有任何问题,请在评论中告诉我。
编码愉快! ?????
以上是不要让你的单身人士崩溃!下面是如何在 Java 中使其线程安全的详细内容。更多信息请关注PHP中文网其他相关文章!