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