我最近和一位朋友就这样的代码争论过:
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,但我无法确定我可以用作支持我的陈述的内容。谁能帮我?谢谢!
答案 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
之前。关于作为私有实例字段的变量的位是无关紧要的。