I have stumbled upon an annoyance as I was writing a Java class; I couldn't figure out how to make copying a 2 dimensional array thread safe. This is the simple version of the class:
public class Frame {
private boolean[][] content;
public Frame(boolean [][] content) {
boolean[][] threadSafeCopy = deepCopy(content);
if (!(threadSafeCopy != null)) {
throw new NullPointerException("content must not be null");
}
if (!(threadSafeCopy.length > 0)) {
throw new IllegalArgumentException("content.length must be greater than 0");
}
if (!(threadSafeCopy[0] != null)) {
throw new IllegalArgumentException("content[0] must not be null");
}
if (!(threadSafeCopy[0].length > 0)) {
throw new IllegalArgumentException("content[0].length must be greater than 0");
}
for (int i = 1, count = threadSafeCopy.length; i < count; ++i) {
if (!(threadSafeCopy[i].length == threadSafeCopy[0].length)) {
throw new IllegalArgumentException
( "content[" + i + "].length [" + threadSafeCopy[i].length
+ "] must be equal to content[0].length [" + threadSafeCopy[0].length + "]"
);
}
}
this.content = threadSafeCopy;
}
private boolean[][] deepCopy(boolean[][] content) {
boolean[][] result = null;
if (content != null) {
synchronized(content) { //do our best to make this as multi-threaded friendly as possible
result = new boolean[content.length][];
for (int i = 0, count = content.length; i < count; ++ i) {
if (content[i] != null)
{
synchronized(content[i]) {
result[i] = content[i].clone();
}
}
}
}
}
return result;
}
public boolean[][] getContent() {
boolean[][] result = new boolean[this.content.length][]; //defensive copy
for (int i = 0, count = result.length; i < count; ++i) {
result[i] = this.content[i].clone(); //defensive copy
}
return result;
}
}
However, the above implementation for the method private boolean[][] deepCopy(boolean[][] content)
isn't actually threadsafe. It's possible that the array is being actively modified by another thread while this method is attempting the copy. Granted, I have guarded against the most abusive case, using synchronized
on the base array. However, that doesn't cause the set of 2nd dimension array instances to be locked. And it's possible for them to be modified during the copy.
Is there is some way to collect the Object lock for each of the base array (content
) and the sub-arrays (content[0]
, content[1]
, ..., content[content.length - 1]
) such that I can call something like synchronized(objectsToLockSimultaneouslyList)
and it lock all the objects simultaneously and in the list's order. If so, I can thread safely copy the contents of the array.
If not, what other kinds of solutions are available to "block all modifications to the array" without having to go change the classes which instantiate Frame or altering Frame's constructor such that it won't take an array, but only instances of immutable collections (which itself is down another freakin rabbit hole).
Thank you for any guidance you have in this area.
UPDATE:
What I want to do is fundamentally not possible. And my understanding of locking objects via synchronized was also erred (tyvm glowcoder, Paulo and Brian). I am now going to attempt to change the interface on Frame to use List<List<Boolean>>
which sure seems like it would be SO much more inefficient. Or I may use Set<XyCoordinate>
where the presence of an XyCoordinate means "true". Again, that seems so freakin inefficient, but threadsafe. Ugh!
> is terrible. What about ImmutableList? The former exists in Guava, the latter would be a simple wrapper for BitSet.
– maaartinus Mar 28 '11 at 13:00