SwiftMango
SwiftMango

Reputation: 15294

How to do "synchronized-with-resource" where obtaining the lock object must also be synchronized?

I have a Map<String, Mutex> m that I would like to lock on the Mutex, while also ensuring that the act of obtaining the lock is thread-safe:

//Assuming till here m.get(key) return a non-null value
synchronized (Mutex mtx = m.get(key)) {
  mtx.count++;
  //...
}

The following is not guaranteed in Java

Mutex mtx = m.get(key);
synchroinzed (mtx) {
  //thread 2 may acquire lock before thread 1 even thread 1 execute first
}

and neither is

synchronized (m.get(key)) {
  Mutex mtx = m.get(key); 
  //mtx could be null if other threads remove the key between 2 statements
  //because lock is only acquired on m.get(key), not m itself
}

How can I safely achieve this?


Edit:

My exact code right now:

public static <T, V> void lock (Map<T, Mutex<V>> mm, T uniqueKeyValue1, long waitMs) throws Mutex.WaitTimeoutException, InterruptedException {

    synchronized ( MapUtils.putIfAbsentRetNewVal(mm, uniqueKeyValue1, new Mutex<V>()) ) {

        Mutex<V> m = mm.get(uniqueKeyValue1);

        if (m.getLockCount() == 0) {
            m.incrementLockCount();
        }
        else {
            m.incrementLockCount();

            m.wait(waitMs);

            if (m.getValue() == null) {
                throw new Mutex.WaitTimeoutException();
            }
        }
    }

}

Upvotes: 1

Views: 150

Answers (3)

user207421
user207421

Reputation: 310979

This is a non-problem, and it doesn't have anything to do with with-resources. Use option 2. What you're asking for won't solve that problem. What you would need is not a with-resources but something that would synchronize on the map, then synchronize on the mutex, then release the synchronization on the map. You can't accompish that with synchronization because of the static scoping.

What you need is in fact another Mutex. Or, just accept the race. It's benign. You don't care which thread executes first.

Upvotes: 1

OscarRyz
OscarRyz

Reputation: 199274

In this scenario you have to lock on the map so no other thread can modify it while the current thread is using it ( m.get(key) ), perform your operation and then release the lock.

Map<String, Mutex> m = ...
synchronized( m ) { // only a single thread can access m now 
   Mutex mtx = m.get(key);
   mtx.count++;
} // releases the lock

Upvotes: 0

Tim Bender
Tim Bender

Reputation: 20442

The language need not have a "synchronize with resources" construct because the JVM guarantees that it will release the lock when the synchronized block exits.

Upvotes: 0

Related Questions