当前位置: 首页 > 知识库问答 >
问题:

是否有任何需要添加易失性关键字来保证java中线程安全的单例类?

万嘉石
2023-03-14

根据这篇文章,线程安全的单例类应该如下所示。但我想知道是否有必要将volatile关键字添加静态单例实例变量中。因为如果实例被创建并存储在CPU缓存中,此时它不会写回主存,同时,另一个线程调用getInstance()方法。它会引起不一致的问题吗?

public class CrunchifySingleton {

    private static CrunchifySingleton instance = null;

    protected CrunchifySingleton() {
    }

    // Lazy Initialization
    public static CrunchifySingleton getInstance() {
        if (instance == null) {
            synchronized (CrunchifySingleton.class) {
                if (instance == null) {
                    instance = new CrunchifySingleton();
                }
            }
        }
        return instance;
    }
}

共有3个答案

柴坚诚
2023-03-14

您如何引用它的代码在Java中是不完整的。是的,您需要volatile和至少Java5才能使双重检查的习惯用法线程安全。您还应该在惰性初始化中添加一个局部变量以提高性能。请在此处阅读更多信息:https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java

糜博远
2023-03-14

是的,如果你的Singleton实例不是易失性,或者即使它是易失性,但是你使用了足够旧的JVM,那么没有对行中的操作的排序保证

instance = new CrunchifySingleton();

根据易失性存储进行分解。

然后,编译器可以对这些操作重新排序,使您的实例不为null(因为已分配内存),但仍然未初始化(因为尚未执行其构造函数)。

如果您想了解更多关于双重检查锁定的隐藏问题,特别是在Java中,请参阅“双重检查锁定被破坏”声明。

lazy holder习惯用法是一个很好的模式,可以很好地概括一般静态字段延迟加载,但是如果您需要一个安全简单的单例模式,我推荐Josh Bloch(来自Efficial Java fame)推荐的Java Enum单例:

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() { ... }
}
西门鹏程
2023-03-14

我附和上面@duffymo的评论:懒惰的单身汉远没有他们最初看起来那么有用。

但是,如果必须使用惰性实例化的单例,那么惰性持有者习惯用法是实现线程安全性的一种更简单的方法:

public final class CrunchifySingleton {
  private static class Holder {
    private static final CrunchifySingleton INSTANCE = new CrunchifySingleton();
  }

  private CrunchifySingleton() {}

  static CrunchifySingleton getInstance() { return Holder.INSTANCE; }
}

另外,请注意,要成为真正的单例,类需要禁止实例化和子类化-构造函数需要是私有的,类需要分别是最终的

 类似资料:
  • volatile的写操作,无法保证线程安全。例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1对count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

  • 本文向大家介绍java枚举是如何保证线程安全的,包括了java枚举是如何保证线程安全的的使用技巧和注意事项,需要的朋友参考一下 前言 写在前面:Java SE5提供了一种新的类型-Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。本文将深入分析枚举的源码,看一看枚举是怎么实现的,他是如何保证线程安全的

  • 问题内容: 有关Singletons的维基百科文章提到了一些用线程安全的方法来用Java实现结构。对于我的问题,让我们考虑具有冗长的初始化过程并且一次被多个线程访问的Singleton。 首先,这个未提及的方法是线程安全的吗?如果是的话,它在什么上进行同步? 其次,为什么以下实现线程安全且在初始化时是懒惰的?如果两个线程同时进入该方法,到底会发生什么? 最后,在第二个示例中,如果一个线程首先获取一

  • 最近我在读一些关于java并发的书。关于线程安全,如果不可能使一个类变为inmutable,那么可以通过同步它的数据来确保线程安全。 下面的类显然不是线程安全的 然后我可以同步写,但它不会保持线程安全 因为我不仅需要同步写入,还需要同步读取 现在的问题是,通过使用易失性,我可以保证其他线程会看到更新的值,所以这让我认为这个类应该是线程安全的 最后一个类线程安全吗??

  • 问题内容: 我需要在性能关键的环境中使用MessageDigest对来自多个线程的多个键进行哈希处理。我知道MessageDigest不是线程安全的,因为它在其对象中存储其状态。什么是实现密钥的线程安全哈希的最佳方法? 用例: 特别: ThreadLocal是否可以保证正常工作?它会有性能损失吗? getInstance返回的对象是否不同,并且它们不会互相干扰?文档说“新”对象,但是我不确定它是否

  • 我注意到,implements在JavaScript中是一个保留关键字。然而,我还没有发现该关键字的任何用法。事实上,我知道JavaScript中没有接口的概念,这与Java等其他编程语言不同,Java在实现接口时使用