1

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!

dsolimano
  • 8,870
  • 3
  • 48
  • 63
chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86
  • What you want is impossible. Replace arrays e.g. by a `ImmutableList` from Guava if you can. Alternatively, do not expose them to other threads, give them a copy to play with. Use defensive copy when accepting arguments from the outside. – maaartinus Mar 28 '11 at 01:42
  • @maaartinus: Got it. Just read the other answers and comments. So, basically there is no possible threadsafe way to use an array as a parameter to my constructor. This means I will just have to change my constructor to not use an array. That, of course, means that I have to find some way to verify I am being passed an immutable List of immutable Lists of Boolean which just seems incredibly inefficient. – chaotic3quilibrium Mar 28 '11 at 01:51
  • @chaotic3quilibrium Right, List> 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
  • Maybe your understanding of "thread-safe" is the culprit. You may use *any object* in a thread-safe manner, assuming that you always synchronize properly. Immutable classes make it all trivial, but they're not always necessary. Don't consider the rest of the program as your enemy trying to modify your objects concurrently in order to trouble you. – maaartinus Mar 28 '11 at 13:05
  • Actually you could just wrap your boolean[][] in a thread-safe wrapper with synchronized methods like `get(int x, int y)` and `set(int x, int y, boolean value)`. Just don't implement any array returning method as it would return a mutable thing. Or maybe do it, but return a clone, see [here](http://www.javapractices.com/topic/TopicAction.do?Id=15). – maaartinus Mar 28 '11 at 13:10
  • @maaartinus: I really like your points. I will look into the options you presented. And to your last comment, that is basically what Frame is supposed to be an immutable Object encapsulating the boolean[][]. Hence the final method getContent() returning a defensive deep copy. – chaotic3quilibrium Mar 28 '11 at 15:10
  • @chaotic3quilibrium I see. I'd suggest not to synchronize on any publicly visible object and use an own lock instead. Look at the solution by Cameron Skinner, possible add a method like `void set(boolean[][] data)` using a write lock. – maaartinus Mar 28 '11 at 16:19
  • @chaotic3quilibrium See also [my question](http://stackoverflow.com/questions/5462150/a-thread-safe-holder-for-arbitrary-cloneable-data). – maaartinus Mar 28 '11 at 16:52

4 Answers4

1

I think your best bet is to wrap the array in an object and provide synchronized access to it. Then you can do the deep copy with the "door shut" to the accessor method.

void deepCopy(boolean[][] orig) {
    synchronized(orig) {
        boolean[][] result = new boolean[orig.length][];
        deepCopy(orig,0);
        return result;        
    }
}

/**
 * recursive method to lock all rows in an array, and then copy
 * them in (backwards, by chance)
 */
void deepCopy(boolean[][] orig, int row) {
    if(row == orig.length) return; // end condition
    synchronized(orig[row]) { // lock the row first
        deepCopy(orig,row+1); // lock the next row
        content[row] = new boolean[orig[row].length];
        for(int i = 0; i < content[row].length; i++)
            content[row][i] = orig[row][i];
        // now, do row - 1
    }
}
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • @glowcoder: I don't understand what you are saying. The purpose of the Frame object is to encapsulate the array for exactly the purpose you are stating, i.e. to create an immutable instance with restricted access (read-only) through the getContent() method. My challenge is that I am having an issue with multi-threaded exposure during the constructor while attempting to establish the initial state. Could you please give me more details, or update your answer with some sort of code sample indicating how you would modify my code? – chaotic3quilibrium Mar 28 '11 at 01:20
  • @c3 Then no, there isn't. Until the data is protected, it's not protected! You could try to acquire all the locks in a row before you ever start copying it though. I'll put something in my answer. It will be slightly off, but maybe you can pick up where I leave off. – corsiKa Mar 28 '11 at 01:28
  • @c3 There - this is about as good as I can imagine it getting. You go through and lock every row, and then fill them in as you return. This will fill up your stack, so it might not scale to giant arrays. – corsiKa Mar 28 '11 at 01:36
  • @glowcoder: Got it. Tyvm! Even if I didn't have a stack problem, that still has some threadsafety exposure. UGH! I am going to flip to Lists and the unreliable immutability available there. – chaotic3quilibrium Mar 28 '11 at 01:55
  • The `boolean[][]` will work just fine, as long as you only ever use the `Frame` object. Wherever the `boolean[][]` comes from, replace it with a thread-safe object (like your `Frame` object) and then refactor where it's used to use that. **This will introduce performance overhead** - your BEST bet would be to make it not accessible from multiple threads. – corsiKa Mar 28 '11 at 01:58
  • @glowcoder: That makes sense. I will just mark this particular constructor as not threadsafe (does not attempt to guard/protect array argument prior to content copy). Tyvm! – chaotic3quilibrium Mar 28 '11 at 02:02
  • That's the best approach you can take. Just simply say "Look, I will do my best to protect your data in a thread safe manner. If you mess it up before I can get to it, there's nothing I can do!" – corsiKa Mar 28 '11 at 02:09
1

You seem to have some misunderstanding about object locks.

A synchronized block (or method) on some object does not avoid any modifications on this object. It only avoids that other threads at the same time are in synchronized blocks or methods of the same object (with the exception on ones in wait() of the same object).

So, to be thread-safe here, you have to make sure all your accesses on the array synchronize on the same lock object.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • @Paulo: I am confused by what you are saying. I am using synchronized(content) on the reference of the object. Given I have done so, I am assuming other threads cannot access or modify content. Are you saying that other threads CAN access and modify content while my thread is executing inside the synchronized(content) block? If so, what's the purpose of the synchronized command? And if so, then what is the way to make arrays multi-thread safe? – chaotic3quilibrium Mar 28 '11 at 01:30
  • 2
    @chaotic3quilibrium: "Given I have done so, I am assuming other threads cannot access or modify content" Nope. Taking the lock means no other thread can take the lock, not that the object associated from the lock is unmodifiable. If you want that property, YOU are responsible to ensure that no access to the object is possible without first taking the lock. Have a look at this tutorial: http://download.oracle.com/javase/tutorial/essential/concurrency – andersoj Mar 28 '11 at 01:37
  • @c3 He is correct. Just because it's synchronized doesn't mean it can't be modified - that just means it can't be waited on again. The purpose is to prevent it from being waited on, and it's up to the programmer to ensure all accesses happen in a synchronized block. – corsiKa Mar 28 '11 at 01:38
  • For sure, other threads CAN modify whatever they want. This is not how `synchronized` works. By synchronizing on something you prevent all other threads from synchronizing on the same object at the same time. That's all. It has nothing to do with them modifying the same object or whatever. – maaartinus Mar 28 '11 at 01:39
  • 1
    **The synchronized contract only works if everyone follows it.** That's why encapsulation is so important - it's easy to "break your end of the bargain" for synchronization. – corsiKa Mar 28 '11 at 01:39
  • @Paulo and c3: Got it. Wow, what a huge gap for me on this. I appreciate the direct feedback. Obviously what I want is not possible. This means I will just have to change my constructor to not use an array. That, of course, means that I have to find some way to verify I am being passed an immutable List of immutable Lists of Boolean which just seems so incredibly inefficient. And I don't think there is a good way to check for immutability in the lists passed to me. UGH! – chaotic3quilibrium Mar 28 '11 at 01:53
1

I'd recommend that you look into using the java.util.concurrent package. It has a bunch of useful classes that might help you.

For your specific example, you can write a simple ThreadSafe2DArray class:

public class ThreadSafe2DArray {
    private ReadWriteLock readWriteLock;
    private Lock readLock;
    private Lock writeLock;
    private boolean[][] data;

    public ThreadSafe2DArray(int rows, int cols) {
        this.data = new boolean[rows][cols];
        this.readWriteLock = new ReentrantReadWriteLock();
        this.readLock = readWriteLock.readLock();
        this.writeLock = readWriteLock.writeLock();
    }

    public boolean get(int row, int col) {
        try {
            readLock.lock();
            return data[row][col];
        }
        finally {
            readLock.unlock();
        }
    }

    public void set(int row, int col, boolean b) {
        try {
            writeLock.lock();
            data[row][col] = b;
        }
        finally {
            writeLock.unlock();
        }
    }

    public boolean[][] copyData() {
        try {
            readLock.lock();
            // Do the actual copy
            ...
        }
        finally {
            readLock.unlock();
        }
    }
}

You can also expose the raw data array and the read/write locks if you want to allow clients to do their own locking, for example if clients could be reading or updating large numbers of entries in one chunk.

This approach is a lot more flexible than using raw synchronized blocks and may be faster or better for your case, although a lot depends on what sort of contention you expect in practice. The added benefit of this is that you don't need to force anything to be immutable.

Fundamentally, if two threads are ever going to be contending for a resource they must agree on some kind of locking mechanism. If the thread that populates the data array does not play nicely then the only option is to have a wrapper that forces locking. If the other thread is well-behaved then you could also synchronize on the top-level array object in both threads, or you can use the expose-locks-and-data approach, or just use the class as presented above and call copyData in your Frame class.

This answer got a lot longer than I intended. Hopefully it makes sense.

EDIT: Note that it's important to put the unlock call inside a finally block in case an unexpected RuntimeException (or an Error) gets thrown somewhere inside the try. In this case the code is pretty simple so it's probably overkill, but it will make the code more maintainable if this safety net is already in place.

EDIT2: I note that you specifically mentioned an irregular array. I've left the example as a regular array in the interests of brevity :)

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
  • Tyvm for being so explicit about how to handle the array itself. I am trying to create an immutable class which encapsulates the array. And I am attempting to stop multi-threaded abuse with the parameter while copying it. Per maaartinus, I just need to not worry as much about it that. I now plan to mark that constructor as "not-threadsafe" and why. And then offer some threadsafe alternative constructors. – chaotic3quilibrium Mar 29 '11 at 14:11
0

I really think you're not grasping some core concepts of multithreading.

If some other thread has a reference to the array you're passing into the Frame constructor, nothing you do in Frame can help you. That other thread can modify it at will.

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • @c3 - If you absolutely need that two dimensional array to be thread safe, you want to create a wrapper class for it. That class creates the array, and has synchronized getters/setters for accessing it; so you'd have like `public synchronized void set(int x, int y, boolean)` That way only one thread could ever be modifying it at once. – Brian Roach Mar 28 '11 at 01:59