Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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!

like image 946
Marc G Avatar asked Feb 28 '16 13:02

Marc G


People also ask

How is the visibility of a variable controlled in Java?

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.

What is visibility in class in Java?

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.

What is visibility problem in Java?

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.

What will be the visibility of a public class?

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.


2 Answers

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.

like image 170
Sergei Tachenov Avatar answered Sep 28 '22 09:09

Sergei Tachenov


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.

like image 43
Joni Avatar answered Sep 28 '22 08:09

Joni