Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
以下java代码在没有volatile的情况下是线程安全的吗?_Java_Multithreading_Parallel Processing_Synchronization_Volatile - Fatal编程技术网

以下java代码在没有volatile的情况下是线程安全的吗?

以下java代码在没有volatile的情况下是线程安全的吗?,java,multithreading,parallel-processing,synchronization,volatile,Java,Multithreading,Parallel Processing,Synchronization,Volatile,有人说,singleton变量的novolatile是错误的。但我认为这是创建单例对象的正确代码。我想知道此代码是否具有线程安全性?您的代码中的变量不需要volatile。但是您的代码有一些性能缺陷。每次get中的代码都会在synchronized块中运行,这几乎不会造成性能开销。您可以使用双重检查锁定机制来避免性能开销。下面的代码展示了使用双重检查锁定机制在java中创建线程安全单例的正确方法 public static Singleton singleton; public static

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

您的代码中的变量不需要volatile
。但是您的代码有一些性能缺陷。每次
get
中的代码都会在
synchronized
块中运行,这几乎不会造成性能开销。您可以使用双重检查锁定机制来避免性能开销。下面的代码展示了使用双重检查锁定机制在java中创建线程安全单例的正确方法

public static Singleton singleton;

public static Singleton get(){
    synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
    } 
    return singleton;
}
有关更多详细信息,请访问此部分并滚动到底部部分“使用Volatile修复双重检查锁定”。

如前所述,您应该将字段
设置为私有,以避免对该字段进行不必要的非线程安全访问

此外,即使在:

  class Singleton {
    private static volatile Singleton singleton = null;
    public static Singleton get() {
        if (singleton == null) {
            synchronized(this) {
                if (singleton == null)
                    singleton = new Singleton();
            }
        }
        return singleton;
    }
}
返回单例
synchronized
块之外,此代码仍然是线程安全的,因为剩余的代码在
synchronized
块内,因此,该块内的所有线程都将强制使用“发生在之前”关系(即,如果实例设置正确,线程将无法返回null)

要注意:引用

只要对singleton的实际写入发生在开始之前 在同步块中,一切正常它只起作用 因为最多有一次写入,所以在 返回。如果有可能进行更多的写操作,它们可能同时发生 指向同步块外部的返回语句

有一个完整的解决方案可以解决为什么在
synchronized
块之外保留
返回单例
是线程安全的

尽管如此,我同意其他用户的相同观点,例如

因为返回不需要任何CPU或任何东西,所以没有理由 为什么它不应该在同步块内。如果是那时 如果我们在单例中,该方法可以标记为synchronized 在这里上课。这将是更干净,更好的情况下,单身得到 在别处修改

也就是说,您不需要volatile子句,因为您正在同步变量
singleton
的初始化。在此上下文中,
synchronized
子句不仅保证多个线程不会访问:

public static Singleton get(){
    synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
    } 
    return singleton;
} 
而且每个线程都可以看到
单例
字段的最新引用。因此,将不同对象实例分配给
singleton
字段的多线程的争用条件将不会发生

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


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

。如果使用
synchronized
块对变量/字段进行线程安全访问,则如果在同一锁下执行读写操作(在同一对象的监视器上进行同步),则代码是正确的。因此,在代码中,您应该防止由“private”修饰符而不是“public”声明读取正常(没有任何内存障碍)

    if (singleton == null) {  
        singleton = new Singleton();  
    }  
您可能会注意到,最后一次读取
返回单例
是正常的,但它是在第一次同步读取(如果singleton不为null)或写入(如果为null)之后的后续读取(按程序顺序),并且不需要放置在同步块中,因为一个线程的PO->HB( “如果x和y是同一线程的操作,并且x在程序顺序中位于y之前,那么hb(x,y)”)

但在我看来,以下结构似乎更为惯用

private static Singleton singleton; // now we don't have direct access (to read/write) the field outside

public static Singleton get(){
    synchronized (Singleton.class) { // all reads in the synchronized Happens-Before all writes
        if (singleton == null) { // first read
            singleton = new Singleton(); // write
        }
    } 
    return singleton; // the last normal read
}
或者只是

// all access is under the lock
synchronized (Singleton.class) {  
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
 }
现在代码是正确的,但可能效率不高。这就引出了这个成语。您可以在中找到对该问题的良好回顾

顺便说一句,有时甚至以下代码也可以:

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

此代码没有数据竞争,而是竞争条件。它总是返回一个Singleton实例,但不总是相同的实例。换句话说,在第一次调用
get()
时,我们可能会看到返回的Singleton的不同实例,这些实例会相互重写到
Singleton
字段,但如果您不介意的话:)。。。(例如,具有相同状态的小型不可变/只读单例)

不清楚为什么您认为他们的代码可以生成两个实例。此代码已正确同步。因此,它将很好地工作;但是,如果您不想在每次获得值时为进入锁付出代价,则有性能更好的替代方案。这就是双重检查锁定问题产生的原因,然后您很快就会得到一个volatile。
private static volatile Singleton singleton;

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