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. I said he would need to make value
a volatile field.
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. Can anyone help me? Thanks!
1) Any variable declared in a method is only visible in that method. (method-local). The programmer has no choice in that. 2) Any variable declared with the modifier private is visible only from within instances of the class it is delared in.
Interfaces and Visibility Interfaces behave like classes within packages. An interface can be declared public to make it visible outside of its package. Under the default visibility, an interface is visible only inside of its package. There can be only one public interface declared in a compilation unit.
The problem with threads not seeing the latest value of a variable because it has not yet been written back to main memory by another thread, is called a "visibility" problem. The updates of one thread are not visible to other threads.
A public class is accessible to all and most visible, try to keep public only key interfaces, never let the implementation go public until you believe it's complete and mature. The private type, on the other hand, is less visible, and in Java, only the nested class or interface can be private.
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.
From Executor docs. Which means that whatever you do before the call to submit
is visible in the 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.
This is one of the features that make java.util.concurrent package very useful.
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
. The start of the thread also happens-before the first action within the thread. Therefore it also happens-before the call to 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. 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.
Since happens-before is transitive, the variable initialization happens-before doStuffWithValue
. The bit about the variable being a private instance field is irrelevant though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With