views:

1288

answers:

6

I fill a collection one single time when my J2EE webapp starts. Then, several thread may access it at same time but only to read it.

I know using a synchronized collection is mandatory for parallels write but do I still need it for parallels read ?

+16  A: 

Normally no because you are not changing the internal state of the collection in this case. When you iterate over the collection a new instance of the iterator is created and the state of the iteration is per iterator instance.


Aside note: Remember that by keeping a read-only collection you are only preventing modifications to the collection itself. Each collection element is still changeable.

class Test {
    public Test(final int a, final int b) {
        this.a = a;
        this.b = b;
    }

    public int a;
    public int b;
}

public class Main {

    public static void main(String[] args) throws Exception {
        List<Test> values = new ArrayList<Test>(2);
        values.add(new Test(1, 2));
        values.add(new Test(3, 4));

        List<Test> readOnly = Collections.unmodifiableList(values);
        for (Test t : readOnly) {
            t.a = 5;
        }

        for (Test t : values) {
            System.out.println(t.a);
        }
    }

}

This outputs:

5
5


Important considerations from @WMR answser.

It depends on if the threads that are reading your collection are started before or after you're filling it. If they're started before you fill it, you have no guarantees (without synchronizing), that these threads will ever see the updated values.

The reason for this is the Java Memory Model, if you wanna know more read the section "Visibility" at this link: http://gee.cs.oswego.edu/dl/cpj/jmm.html

And even if the threads are started after you fill your collection, you might have to synchronize because your collection implementation could change its internal state even on read operations (thanks Michael Bar-Sinai, I didn't know such collections existed).

Another very interesting read on the topic of concurrency which covers topics like publishing of objects, visibility, etc. in much more detail is Brian Goetz's book Java Concurrency in Practice.

smink
This answer is not wrong but is also not sufficient. The answer from @WMR has some additional important caveats. In particular, you need to consider the issue of making sure that the reader threads will actually see the values written at startup either through safe publication or some memory barrier
Alex Miller
See @WMR's and my answers below... this answer is wrong, as get() actions could transform internal state in the collections (e.g. caches, last-touched statistics, etc).
Michael Bar-Sinai
@Alex Miller: I think the answer is not only not sufficient, it clearly says "No" without listing any conditions for this no. And this is wrong (and as you probably know dangerous if you rely on it).
WMR
You also have the added complexity that a Collection can be implemented using stateful or non-threadsafe objects. This can occur when dealing with certain ORM/JPA collections that lazily load objects.
James Schek
A: 

The collection itself does not, but keep in mind that if what it holds is not immutable also, those seperate classes need their own synchronization.

mattlant
Wow, thx. Nice job
mattlant
+3  A: 

You do not have to, as explained in other answers. If you want to ensure that your collection is read only, you can use:

yourCollection = Collections.unmodifableCollection(yourCollection);

(similar method exist for List, Set, Map and other collection types)

Nicolas
+8  A: 

It depends on if the threads that are reading your collection are started before or after you're filling it. If they're started before you fill it, you have no guarantees (without synchronizing), that these threads will ever see the updated values.

The reason for this is the Java Memory Model, if you wanna know more read the section "Visibility" at this link: http://gee.cs.oswego.edu/dl/cpj/jmm.html

And even if the threads are started after you fill your collection, you might have to synchronize because your collection implementation could change its internal state even on read operations (thanks Michael Bar-Sinai, I didn't know such collections existed in the standard JDK).

Another very interesting read on the topic of concurrency which covers topics like publishing of objects, visibility, etc. in much more detail is Brian Goetz's book Java Concurrency in Practice.

WMR
+4  A: 

Have a look at the discussion of a very related question: http://stackoverflow.com/questions/104184/is-it-safe-to-get-values-from-a-javautilhashmap-from-multiple-threads-no-modifi.

Alexander
+5  A: 

In the general case, you should. This is because some collections change their internal structure during reads. A LinkedHashMap that uses access order is a good example. But don't just take my word for it:

In access-ordered linked hash maps, merely querying the map with get is a structural modification The Linked hash map's javadoc

If you are absolutely sure that there are no caches, no collection statistics, no optimizations, no funny stuff at all - you don't need to sync. In that case I would have put a type constraint on the collection: Don't declare the collection as a Map (which would allow LinkedHashMap) but as HashMap (for the purists, a final subclass of HashMap, but that might be taking it too far...).

Michael Bar-Sinai
Interesting .. never knew that some collections change their internal structure during reads.
Jimmy