3
public class TestThread {
ExecutorService executorService = Executors.newFixedThreadPool(10);

public static void main(String[] args) {
    TestThread testThread = new TestThread();
    final List<String> list = new ArrayList<>();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.print(list);
        }
    }
    testThread.executorService.submit(runnable);

}
}

In this code code snippet, a list is created in main thread. And in a runnable instance will access this list in other threads which were managed by ExecutorService.

So my question is if there will be thread memory visibility issue in this case? As I know a thread can not see(or completely see) the value in another thread if sychronization/volatile not being used.

Chuck
  • 1,508
  • 4
  • 15
  • 19

3 Answers3

4

The reason you have to make that variable final is that your Runnable can be created with a copy of it (which is also final).

final fields initialized during object construction are guaranteed to be published properly to all threads.

So it will see the list.

However, ArrayList is not thread-safe, so you should not be sharing it with multiple threads (if you plan to modify it).

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • Saying "created with a copy of it" could be taken to mean that the *list* is copied (rather than just the reference to it). Would you consider clarifying that a little? – Andy Turner Mar 04 '16 at 07:52
  • Yes, I agonized over how to phrase it. Feel free to edit. http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class?rq=1 – Thilo Mar 04 '16 at 07:54
  • I couldn't think of a concise way to do it, so I've added an answer explaining it. Why say 1 word when 1000 would do? – Andy Turner Mar 04 '16 at 08:11
2

I think you've only partially understood the problem. All threads would be able to "see" the list. The problem is what they "see". Unless you either make sure that the list is not modified once the threads are started or make sure to synchronize access to the list (or any data structure that is accessed by multiple threads) then you run the risk of experiencing inconsistencies or in the worst case memory corruption.

The volatile keyword is not enough. You will need to synchronize access to the list using synchronized blocks of code, or some other synchronization primitive (monitor, semaphore etc.)

Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
2

It may be useful to show a little about how anonymous classes are implemented, and how they access local variables.

Your Runnable class here would be converted by the compiler into a new "named" class, which looks something like:

class TestThread$1 implements Runnable {
  private final List<String> list;

  TestThread$1(List<String> list) {
    this.list = list;
  }

  @Override
  public void run() {
    System.out.println(list);
  }
}

and the line where you instantiate it would be converted to:

Runnable runnable = new TestThread$1(list);

As such, the list reference used inside the anonymous class is actually not the same reference as the list reference in the main method - it just so happens to point to the same instance, and has the same name.

The requirement for variables referred to in anonymous classes to be final ensures that these two variables will point to the same instance forever after, since you can't reassign either one of them. (See Why are only final variables accessible in anonymous class?)

Now, as noted by Thilo, final member variables are guaranteed to be visible to all threads when the constructor finishes. The above code shows that there is a constructor here, which assigns a final variable. As such, the list reference available inside the body of the run() is guaranteed to be visible and consistent to all threads.

However, this makes no guarantees about the visibility and consistency of the list's internal variables. As stated in the Javadoc for ArrayList:

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally.

Hence, external synchronization may be required, depending upon what you do with the list in any of the threads which have a reference to it (whether that is the main thread or any thread running the Runnable).

Community
  • 1
  • 1
Andy Turner
  • 137,514
  • 11
  • 162
  • 243