首页 > 解决方案 > 以下java代码线程安全没有volatile吗?

问题描述

public static Singleton singleton;

public static Singleton get(){
    synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
    } 
    return singleton;
}

有人说singleton变量没有 volatile 是错误的。但我认为这是创建单例对象的正确代码。我想知道这段代码是否具有线程安全性?

标签: javamultithreadingparallel-processingsynchronizationvolatile

解决方案


正如anatolyg指出的那样,您应该将该字段singleton设为私有,以避免对该字段进行不必要的非线程安全访问。

此外,即使在:

public static Singleton get(){
    synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
    } 
    return singleton;
} 

在块return singleton;之外,这段代码仍然是线程安全的,因为剩余的代码在块内,因此,该块内的所有线程都将强制发生之前的关系(即,如果线程无法返回 null实例已正确设置)。synchronizedsynchronized

话虽如此,请注意:引用Holger

只要对单例的实际写入发生在同步块开始之前,一切正常。它之所以有效,是因为最多只有一次 write,肯定是在返回之前执行的。如果可以进行更多写入,则它们可能会同时发生在同步块之外的 return 语句中。

有一个完整的SO Thread解决了为什么离开块return singleton外部是线程安全的synchronized

尽管如此,我与其他用户(例如这个用户)有着相同的看法

由于返回不占用任何 CPU 或任何东西,因此没有理由不应该在同步块内。如果是,那么如果我们在此处的 Singleton 类中,则可以将该方法标记为同步。如果单例在其他地方被修改,这会更干净更好。

话虽如此,您不需要volatile子句,因为您正在同步变量的初始化singleton。在这种情况下,该synchronized子句不仅保证多个线程不会访问:

    if (singleton == null) {  
        singleton = new Singleton();  
    }  

singleton而且每个线程都可以看到该字段的最新参考。因此,不会发生多个线程将不同对象实例分配给该字段的竞争条件。singleton

有人说单例变量没有 volatile 是错误的。

可能这个人将您的代码误认为是双重检查锁定模式,这是对您展示的版本的性能优化。在您的版本中,线程将在每次调用方法时进行同步get,这在变量正确初始化后就没有必要了。singleton这是双重检查锁定模式试图避免的开销。为了实现需要volatile (您可以阅读有关此SO Thread的深入解释),可以在此处找到有关此双重检查锁定模式的更多信息。


推荐阅读