views:

303

answers:

2

Hi,

I have two arrays, and I need to synchronize access to them across threads. I am going to put them in a synchronized block. The problem is, I can pass only one of them to 'synchronized' st one go.

How do I ensure that the access to both the arrays is synchronized? Do I put them in a class and create an object of it? Or I access the other array only in the synchronized block, and this takes care of synchronized access to it?

Thanks,

+11  A: 

Whatever you do don't do this:

synchronized (array1) {
  synchronized (array2) {
    // do stuff
  }
}

That's a sure recipe for deadlock.

Basically what you have to do is create one lock object that you will use if you want to access either array and then use that for all array access. It's coarse-grained but safe. You could do it this way:

public static class TwoArrays {
  private int[] array1 = ...
  private int[] array2 = ...
  private final Object LOCK = new Object();

  public void doUpdate() {
    synchronized (LOCK) {
      ...
    }
  }
}

If you need a finer-grained method you want to use the Java 5+ concurrent utilities such as ReadWriteLock but this will be more complicated to implement and error-prone.

cletus
You might as well make `doUpdate` synchronized, then, rather than have the added complexity of the `LOCK` object.
skaffman
Actually no, it's best practice not to expose your locks, which is what you're doing when you use this as a lock.
cletus
this makes a question arise - using the nested synchronized block can lead to a deadlock, in case there is another synchronization on the same objects. But would it happen when only the code above is present? No thread could acquire the monitor of array2 if it doesn't already have the monitor of array1. And if it has the monitor of array1, no other thread can have the monitor of array2. Do I have a hole in my reasoning?
Bozho
About the first deadlock recipe. Isn't it safe as long as you always have the synchronized blocks in the same order (Admittedly the second example still feels better).
Buhb
@Buhb - yes, that's exactly the problem, when locking isn't always done in the same order.
abyx
@abyx - but you are missing his point. Locking two arrays like that is not a SURE recipe. Its only is a recipe if different threads COULD lock the arrays in different orders. If that cannot happen, the code is perfectly safe.
Stephen C
I downvoted because you're encouraging someone to synchronize on a String instance.
McDowell
I think this String won't be interned, so might not be a problem in this particular case.
Bozho
@Bozho - although it is probably safe to synchronize on this string in this instance, there is no need for it. A new `java.lang.Object` would be smaller and cheaper and do the same job. 99% of the times I see people synchronizing on a `java.lang.String` instance, they're just writing concurrency bugs.
McDowell
@McDowell, those instances probably have nothing to do with the fact that synchronisation is done on a String, but on a method variable instead of an object attribute. The attribute name `LOCK` provides enough information as to the intended use.
rsp
Thanks a lot for the responses! this is what I am doing now:final Object syncObject = new Object();synchronized (syncObject) {//accessing my arrays here }Thanks!
Chaitanya
+6  A: 

Prior to Java 5, I'd have written things like this:

// pre Java 5 code:
Object lock = new Object();
// ...
synchronized(lock) {
    // do something that requires synchronized access
}

But since Java 5, I'd use classes from java.util.concurrent.locks (personally, I don't find this more complicated or error-prone):

// Java 5 Code Using Locks
Lock lock = // ...
lock.lock();
try {
    // do something that requires synchronized access
}
finally {
    lock.unlock();
}

If you need read-write locking, here is example implemented using read-write locks from Java 5:

private ReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock rLock = rwl.readLock();
private Lock wLock = rwl.writeLock();

private List<String> data = new ArrayList<String>();

public String getData(int index) { 
    rLock.lock();
    try {
       return data.get(index);
    } finally {
        rLock.unlock();
    }
}

public void addData(int index, String element) {
    wLock.lock();
    try {
        data.add(index, element);
    } finally {
        wLock.unlock();
    }
}

Of course, adapt it to suit your needs.

Pascal Thivent
Thanks for the replies!!
Chaitanya