I am facing a problem in my program when multiple threads access the same server over RMI. The server contains a list as a cache and performs some expensive computation sometimes changing that list. After the computation finished the list will be serialized and sent to the client.
First Problem: if the list is changed while being serialized (e.g. by a different client requesting some data) a ConcurrentModificationException
is (probably) thrown, resulting in a EOFException
for the RMI call / the deserialization on the client-side.
Therefore I need a some kind of list-structure which is "stable" for serialization while possibly being changed by a different thread.
Solutions we tried:
CopyOnWriteArrayList
- expensive as well since it copies the list andrevealing the Second Problem: we need to be able to atomically replace any element in the list which is currently not thread-safe (first delete, then add (which is even more expensive)) or only doable by locking the list and therefore only doing the different threads in sequence.
Therefore my question is:
Do you know of a
Collection
implementation which allows us toserialize
the Collection thread-safe while other Threads modify itand
which contains some way ofatomically replacing
elements?A bonus would be if the list would
not
need to becopied
before serialization! Creating a snapshot for every serialization would be okay, but still meh :/
Illustration of the problem (C=compute, A=add to list, R=remove from list, S=serialize)
Thread1 Thread2
C
A
A C
C A
S C
S R <---- Remove and add have to be performed without Thread1 serializing
S A <---- anything in between (atomically) - and it has to be done without
S S blocking other threads computations and serializations for long
S and not third thread must be allowed to start serializing in this
S in-between state
S
My wrong initial thought was that the CopyOnWriteArrayList
was a bad idea since it copies everything. But of course it does only perform a shallow copy, copying only the references, not a deep copy copying all Objects as well.
Therefore we clearly went with the CopyOnWriteArrayList
because it already offered a lot of the needed functionality. The only remaining problem was the replace
which even got more complex to be a addIfAbsentOrReplace
.
We tried the CopyOnWriteArraySet
but that did not fit our need because it only offered addIfAbsent
. But in our case we had a instance of a class C
called c1
which we needed to store and then replace with a updated new instance c2
. Of course we overwrite equals
and hashCode
. Now we had to choose wether or not we wanted the equality to return true
or false
for the two only minimally different objects. Both options did not work, because
true
would mean that the objects are the same and the set would not even bother adding the new object c2
because c1
already is infalse
would mean c2
would be added but c1
would not be removedTherefore CopyOnWriteArrayList
. That list already offers a
public void replaceAll(UnaryOperator<E> operator) { ... }
which somewhat fits our needs. It lets us replace the object we need via custom comparison.
We utilized it in the following way:
protected <T extends OurSpecialClass> void addIfAbsentOrReplace(T toAdd, List<T> elementList) {
OurSpecialClassReplaceOperator<T> op = new OurSpecialClassReplaceOperator<>(toAdd);
synchronized (elementList) {
elementList.replaceAll(op);
if (!op.isReplaced()) {
elementList.add(toAdd);
}
}
}
private class OurSpecialClassReplaceOperator<T extends OurSpecialClass> implements UnaryOperator<T> {
private boolean replaced = false;
private T toAdd;
public OurSpecialClassReplaceOperator(T toAdd) {
this.toAdd = toAdd;
}
@Override
public T apply(T toAdd) {
if (this.toAdd.getID().equals(toAdd.getID())) {
replaced = true;
return this.toAdd;
}
return toAdd;
}
public boolean isReplaced() {
return replaced;
}
}