Returning Java Arrays vs Collections
I'm trying to think through some design in regards to memory allocation and multithreading in a java app and this is what I'm wondering:
I have a class that has a synchronized Collection say a list that is getting updated several times a a second but all updates are happening within the class and its own thread not from other threads. However I have many other threads that call the getCollection() method and do a foreach to iterate its contents in a read only fashion. This is what I don't know:
If another thread is iterating the synchronized colletion w开发者_如何转开发ill the single thread that performs the updates have to wait until a point in time when no other threads are iterating?
My second question is it seems to make sense to return an array copy of the collection not the collection itself by doing .toArray but from thinking about it from a memory point of view won't that have to allocate a new array that is the size of the collection contents everytime and if getting called hundreds of times a second on a collection that has several thousand objects in it is something I don't know makes sense or not.
Also if I never return the collection itself than making the list synchronized is no longer necessary?
Would appreciate any input. Thanks! - Duncan
if another thread is iterating the synchronized colletion will the single thread that performs the updates have to wait until a point in time when no other threads are iterating?
If you're talking about synchronized (not concurrent) collections then yes.
As for the second question, it
looks like a real use case for java.util.concurrent.CopyOnWriteArrayList
.
I would suggest you use the CopyOnWriteArrayList. This is thread safe and can be read accessed efficient by any number of threads. Provided you have a small number of updates this should be fine.
However, to answer your questions. If you iterator over a synchronized collection while it is being modifed, you will get a ConcurrentModificationException (COWAL doesn't get this) Your update will not be blocked by this, only your readers will have a problem.
Instead of creating a copy each time getCollection is called, youc an create a copy each time the collection is modifed (far less often) This is what COWAL does for you.
If you return a copy on demand, you will still need to synchronize the collection.
Probably the easiest way to deal with this is to keep two collections: one that is updated by the class itself, and a read-only copy in a volatile field that is returned when getCollection()
is called.
The latter needs to be recreated by the process that updates the main collection when appropiate. This allows you to atomically update your collection: change several elements in one go, while hiding the intermediate states.
If your updates are infrequent and every update leaves the collection in a consistent state, then use the CopyOnWriteArrayList already suggested.
It seems that the collections is being updated frequently and #getCollection() is being called frequently. You could use CopyOnWriteArrayList but you'll creating a copy every time you modify the array. So you'll need to see how this effects performance.
Another option is to task the thread within the class to make a copy everytime #getCollection is called. This will involve #getCollection waiting for the internal class thread to complete.
If you just want #getCollection to return a recent copy and not the most up to date copy then you can have the internal thread periodically create a copy of the collection that gets returned in #getCollection. The copy will need to be volatile or be an AtomicReference.
精彩评论