Reputation: 28840
We are facing the following problem in a Spring service, in a multi-threaded environment:
My initial idea is to make a container object having the three lists as properties.
Then the synchronization would be first on that object, then one by one on each of the three lists.
Some code is worth a thousands words... so here is a draft
private class Sync {
final List<Something> a = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> b = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> c = Collections.synchronizedList(new ArrayList<Something>());
}
private Sync _sync = new Sync();
...
void updateRunOnceEveryFiveMinutes() {
final List<Something> newa = new ArrayList<Something>();
final List<Something> newb = new ArrayList<Something>();
final List<Something> newc = new ArrayList<Something>();
...building newa, newb and newc...
synchronized(_sync) {
synchronized(_sync.a) {
_synch.a.clear();
_synch.a.addAll(newa);
}
synchronized(_sync.b) { ...same with newb... }
synchronized(_sync.c) { ...same with newc... }
}
// Next is accessed by clients
public List<Something> getListA() {
return _sync.a;
}
public List<Something> getListB() { ...same with b... }
public List<Something> getListC() { ...same with c... }
The question would be,
update
Changed the order of _sync synchronization and newa... building.
Thanks
Upvotes: 1
Views: 435
Reputation: 53694
your best option is to expose a "current state" object and use volatile, ditching all the synchronized code. (also, you may want to make the lists unmodifiable wrappers just to be safe).
public class ListState {
List final a;
List final b;
List final c;
}
private volatile ListState _state;
void updateRunOnceEveryFiveMinutes() {
// generate new lists ...
// re-assign current list state (e.g. atomically publish all your updates)
_state = new ListState(newA, newB, newC);
}
public ListState getCurrentLists() {
return _state;
}
Upvotes: 2
Reputation: 310936
The draft doesn't accomplish your objective. There is nothing here that prevents access to the old C while the new A or B are being computed. The simplest way to accomplish that is like so:
synchronized (a) {
synchronized (b) {
synchronized (c) {
}
}
}
around all accesses whether read or write. And I wouldn't do clear() and addAll(), just change the variable value to newA/newB/newC, although that would require some adjustment to the above.
Upvotes: 1
Reputation: 48629
It's a bit wasteful to build a new list only to copy it into another (final) list.
Consider using immutable lists and assigning the new list to _sync.a
, _sync.b
and _sync.c
(having removed the final
modifier from them) after building the list (in updateRunOnceEveryFiveMinutes
.)
Also you are synchronizing on 4 different objects. It would be simpler to synchronize on just one object (_sync
or possibly a dedicated lock object) for each method call.
If the lists returned by getListA
etc. are immutable you do also not need the calls to Collections.synchronizedList
.
Upvotes: 1