java - 在同步方法中读取值时的安全发布

标签 java multithreading thread-safety

我的问题涉及 Java 中字段值的安全发布(如此处讨论的 Java multi-threading & Safe Publication )。

据我了解,如果满足以下条件,则可以安全地读取一个字段(意味着来自多个线程的访问将看到正确的值):

  • 读写在同一台显示器上同步
  • 字段是最终的
  • 字段是可变的

如果我的理解是正确的,下面的类应该不是线程安全的,因为初始值是在没有这些特征的情况下编写的。然而,我发现很难相信我需要制作 first volatile,即使它只能从 synchronized 方法访问。

public class Foo {

    private boolean needsGreeting = true;

    public synchronized void greet() {
        if (needsGreeting) {
            System.out.println("hello");
            needsGreeting = false;
        }
    }
}

我错过了什么吗?上面的代码是否正确,如果正确,为什么?或者在这种情况下是否有必要使 first volatile 或使用 final AtomicBoolean 或类似的另外synchronized 方法访问它。

(只是澄清一下,我知道,如果初始值是用 synchronized 方法编写的,即使没有 volatile 关键字,它也是线程安全的。 )

最佳答案

在构造函数结束和方法调用之间没有发生之前的关系,因此一个线程可以开始构造实例并使引用可用,而另一个线程可以获取该引用并开始调用部分构造的对象上的 greet() 方法。 greet() 中的同步并没有真正解决这个问题。

如果您通过著名的双重检查锁定模式发布一个实例,就会更容易看到如何操作。如果有这种happens-before关系,即使使用DCLP也应该是安全的。

public class Foo {
    private boolean needsGreeting = true;

    public synchronized void greet() {
        if (needsGreeting) {
            System.out.println("Hello.");
            needsGreeting = false;
        }
    }
}

class FooUser {
    private static Foo foo;

    public static Foo getFoo() {
        if (foo == null) {
            synchronized (FooUser.class) {
                if (foo == null) {
                    foo = new Foo();
                }
            }
        }
        return foo;
    }
}

如果多个线程同时调用 FooUser.getFoo().greet() ,一个线程可能正在构造 Foo 实例,但另一个线程可能过早地找到一个非空的 Foo 引用,并调用 greet() 并找到needsGreeting 仍然是 false。

Java 并发实践 (3.5) 中提到了这方面的示例。

关于java - 在同步方法中读取值时的安全发布,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4032792/

相关文章:

java - 如何将数据库中的所有行元素添加到JSON对象

c# - C# 中的原子读-修改-写

java - Bruce Eckel 《Java 思维》第 4 版,ThreadLocal 变量持有者

java - 如何在多实例 Tomcat 环境中以固定间隔运行任务?

java - Server EndPoint for WebSocket Java API 中的 volatile 变量?

java - 在 Java 运行时评估基于 Json 的规则

Java 操作监听器和 JButton

java - 复制 xml 中的 View

multithreading - 访问TObjectList线程的不同索引是否安全?

go - 为什么 os/exec.CombinedOutput() 没有竞争条件?