可以通过另一个线程读取由类的方法分配的延迟的非易失性变量吗?

时间:2015-03-12 04:16:02

标签: java multithreading

请考虑以下代码:

import java.util.concurrent.Callable;

final public class DelayedSet {
    String Val = "Uninitialized";

    public Callable<String> Makegetter(String val) {
        this.Val = val;
        return new Callable<String>() {
            public String call() {
                return DelayedSet.this.Val;
            }
        };
    }
}

class Main {
    public static void main(String[] args) {
        DelayedSet x = new DelayedSet();
        Callable<String> Foogetter = x.Makegetter("Initialized");
        // Version 1
        try {
            System.out.println(Foogetter.call());
        }
        catch (Exception e) {
        }
    }
}

运行Main后,将打印“已初始化”。

现在考虑变体A ,其中Foogetter被传递给新的线程。那么Foogetter是否也会返回“已初始化”,或者由于过时的缓存条件,Foogetter是否可以返回“未初始化”?

还要考虑变体B ,其中我们有三个主题T1T2T3T1,通过期货,向Callable提交T2,其中T2创建DelayedSet,调用Makegetter,然后返回"Foogetter" (在技术上匿名的引号中)通过将来回到T1T1然后获取此结果("Foogetter"),并提交另一个可调用者,这次是T3,其中T3调用"Foogetter"。对于这两种变体,是否保证将返回“已初始化”或者是否可以返回“未初始化”?

总结psuedocode:

T1:
futureT2 = executorService.submit(new Callable {
...
call() {
// Runs in T2
Foo = new DelayedSet;
return Foo.Makegetter("Initialized");
} ...
futureT3 = executorService.submit(futureT2.get());
print(futureT3.get());

来自这个question,我得到的印象是,人们需要依赖同步事件来捎带,例如volatile或synchronized块。但是,我正在尝试确定不需要挥发物的特殊情况(即使是通过捎带),但是由于在线程创建和加入的语义之前发生,不会产生任何过时的缓存条件。

为了回答这个问题,有人可以澄清关于线程的内存模型是什么吗?

1 个答案:

答案 0 :(得分:1)

对于 Variant A ,我会假设

new Thread(() -> {
    try {
        System.out.println(Foogetter.call());
    } catch (Exception e) {
    }
}).start();

在这种情况下,the JLS has us covered

  

在启动线程中的任何操作之前,对线程的start()调用发生。

this.Val = val;

发生在调用Makegetter之前发生的Thread#start()调用中,然后在启动的线程中调用call之前发生。返回的值必须始终为"Initialized"

变体B 中,首先要注意的是Future的内存一致性效果

  

异步计算采取的操作发生在操作之前   在另一个帖子中跟随相应的Future.get()

futureT2.get()返回T1时,call中的T2调用已经发生( - 之前)并且MakeGetter的调用已经设置DelayedSet.Val的值。 T1可以看到此更改,CallableT3会返回此更新后的值,并再次T1futureT3.get()会将其检索到。{/ { p>