简体   繁体   English

在Java中赋值给变量的可见性

[英]Visibility of assignment to variable in Java

I recently argued with a friend on a code like this: 我最近和一位朋友就这样的代码争论过:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * See memory consistency effects in a Java Executor.
 */
public class PrivateFieldInEnclosing {
    private long value;
    PrivateFieldInEnclosing() {}
    void execute() {
        value = initializeValue();
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.submit(new Y());
    }

    class Y implements Runnable {
        @Override
        public void run() {
            System.out.println(value);
        }
    }

    private long initializeValue() {
        return 20;
    }

    public static void main(String[] args) {
        new PrivateFieldInEnclosing().execute();
    }
}

I argued that it's possible that value can be seen as 0 in Y , because there's no guarantee that the assignment value = initializeValue() is visible in the executor's threads. 我认为在Y可能会将value视为0 ,因为无法保证赋值value = initializeValue()在执行程序的线程中可见。 I said he would need to make value a volatile field. 我说他需要把value变成一个不稳定的领域。

He contradicted me, and said that because it's a private instance field with the value assigned before the thread creation, then the value is visible. 他与我发生矛盾,并说因为它是一个私有实例字段,在创建线程之前赋值,所以该值是可见的。

I looked into https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 but I couldn't put my finger on what exactly I can use as backing for my statement. 我查看了https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4,但我无法确定我可以用作支持我的内容声明。 Can anyone help me? 谁能帮我? Thanks! 谢谢!

It's irrelevant whether it's private or not. 它是否私有无关紧要。 What's relevant is this: 与此相关的是:

Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread. 内存一致性效果:在将Runnable对象提交给Executor之前,线程中的操作发生在执行开始之前,可能在另一个线程中。

From Executor docs . 来自Executor文档 Which means that whatever you do before the call to submit is visible in the runnable. 这意味着无论你在submit调用之前做什么都在runnable中可见。 You could even do it after constructing the executor, and in this particular case it doesn't even matter when the executing thread actually starts because the submit method provides a very strong guarantee by itself. 您甚至可以在构造执行程序之后执行此操作,并且在此特定情况下,执行线程实际启动时甚至不重要,因为submit方法本身提供了非常强大的保证。

This is one of the features that make java.util.concurrent package very useful. 这是使java.util.concurrent包非常有用的功能之一。

Your friend would be correct. 你的朋友是对的。 IF the variable initialization is before a call to Thread.start in program order, by JLS 17.4.5 it happens-before the Thread.start . 如果变量初始化是在按程序顺序调用Thread.start之前,则通过JLS 17.4.5它发生在Thread.start之前。 The start of the thread also happens-before the first action within the thread. 线程的开始也发生在线程中的第一个操作之前。 Therefore it also happens-before the call to doStuffWithValue . 因此,它也会在调用doStuffWithValue之前发生。

This particular case cannot be covered by the JLS alone because of the use of Executor : you don't know when it calls Thread.start for the threads it uses. 由于使用了Executor ,因此单独的JLS无法涵盖这种特殊情况:您不知道它何时调用Thread.start来使用它所使用的线程。 But from here you can read that calling submit gives you the same guarantee as Thread.start : Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins. 但是从这里你可以看到,调用submit给你提供了与Thread.start相同的保证: 在执行Runnable之前的一个线程中的动作发生 - 在执行开始之前。

Since happens-before is transitive, the variable initialization happens-before doStuffWithValue . 由于before-before是传递性的,因此变量初始化发生在doStuffWithValue之前。 The bit about the variable being a private instance field is irrelevant though. 关于作为私有实例字段的变量的位是无关紧要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM