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

时间:2016-02-28 13:20:09

标签: java multithreading concurrency

我最近和一位朋友就这样的代码争论过:

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();
    }
}

我认为value 0 Y可能被value = initializeValue()视为value,因为我无法保证作业public void modifyImage(BufferedImage image) { BufferedImage green = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); BufferedImage blue = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); BufferedImage red = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); for(int y= 0; y < image.getHeight(); y ++){ for(int x = 0; x < image.getWidth(); x++){ red.setRGB(x, y, new Color(image.getRGB(x, y)).getRed()); green.setRGB(x, y, new Color(image.getRGB(x, y)).getGreen()); blue.setRGB(x, y, new Color(image.getRGB(x, y)).getBlue()); } } BufferedImage finalImage = new BufferedImage(red.getWidth(), red.getHeight(), BufferedImage.TYPE_INT_RGB); for(int y= 0; y < image.getHeight(); y ++){ for(int x = 0; x < image.getWidth(); x++){ finalImage.setRGB(x, y, new Color(new Color(red.getRGB(x, y)).getRed(),new Color(green.getRGB(x, y)).getGreen(),new Color(blue.getRGB(x, y)).getBlue()).getRGB()); } } this.image = green; } 在执行者的线程中可见。我说他需要让css成为一个不稳定的领域。

他与我发生了矛盾,并说因为它是一个私有实例字段,在创建线程之前赋值,所以该值是可见的。

我调查https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4,但我无法确定我可以用作支持我的陈述的内容。谁能帮我?谢谢!

2 个答案:

答案 0 :(得分:10)

它是否私有无关紧要。与此相关的是:

  

内存一致性效果:提交之前线程中的操作   Exenor的Runnable对象发生 - 在执行开始之前,   也许在另一个主题中。

来自Executor docs。这意味着无论你在submit的调用之前做什么都在runnable中可见。您甚至可以在构造执行程序之后执行此操作,在这种特殊情况下,执行线程实际启动时并不重要,因为submit方法本身提供了非常强大的保证。

这是使java.util.concurrent包非常有用的功能之一。

答案 1 :(得分:1)

你的朋友是对的。如果变量初始化在按程序顺序调用Thread.start之前,则由JLS 17.4.5发生 - 在Thread.start之前。线程的开始也发生在线程中的第一个操作之前。因此,在调用doStuffWithValue之前也会发生这种情况。

由于使用了Executor,因此仅JLS无法涵盖此特定情况:您不知道何时为其使用的线程调用Thread.start。但是从here开始,你可以看到调用submit给你提供与Thread.start相同的保证:在向执行者提交Runnable之前的一个线程中的动作发生在它之前执行开始。

因为before-before是传递性的,所以变量初始化发生在doStuffWithValue之前。关于作为私有实例字段的变量的位是无关紧要的。