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

Joe*_*e23 11 java multithreading thread-safety

我的问题涉及在Java中安全发布字段值(这里讨论Java多线程和安全发布).

据我了解,如果出现以下情况,可以安全地读取字段(意味着从多个线程访问将看到正确的值):

  • 读取和写入在同一监视器上同步
  • 领域是最终的
  • 场是不稳定的

如果我的理解是正确的,那么下面的类不应该是线程安全的,因为初始值是在没有这些特性的情况下编写的.但是我觉得很难相信我需要制作,first volatile即使它只是从一个synchronized方法访问.

public class Foo {

    private boolean needsGreeting = true;

    public synchronized void greet() {
        if (needsGreeting) {
            System.out.println("hello");
            needsGreeting = false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?以上代码是否正确,如果是,为什么?或者在这种情况下,除了从方法访问它之外,还需要制作first volatile或使用final AtomicBoolean类似的东西.synchronized

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

sjl*_*lee 5

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

如果您通过着名的双重检查锁定模式发布实例,则可以更轻松地查看实例.如果存在这种发生在之前的关系,即使使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Java Concurrency in Practice(3.5)中提到了一个这样的例子.