0

Yes the synchronized locking synmtax is easy because of it's block structure but sometimes you can't use it. Is there a way to explicitly lock and unlock the "synchronized" monitor on an object so that it is compatible with prior usages of "synchronized" ?

class MyObj {
    Object locker_ = new Object();
    void lockedMethod() {
       synchronized(locker_) {
           ....
       }
    }
    Iterator lockTraversal() {
        explicitlyWaitForAndAcquireLock(locker_); // assume will not throw exception
        return(getAnIterator());
    }
    void unlockTraversal() {
        explicitlyReleaselock(locker_);
    }
}


 MyObj obj = (...)

 try {
     Iterator it = obj.lockTraversal();
     for(;;) // iterate 
 } finally {
     obj.unlockTraversal();
 }

Of course in this example "closures" would eliminate this need, As would "stack scoped" destructors ;^> But ...

peterk
  • 5,136
  • 6
  • 33
  • 47

5 Answers5

1

You can't do it on an object's intrinsic lock, but you can use java.util.concurrent.locks.Lock instead.

If you really need it, are feeling risky, and aren't worried about portability, you can also use misc.sun.Unsafe, specifically monitorEnter and monitorExit. But... Probably not a good idea.

yshavit
  • 42,327
  • 7
  • 87
  • 124
1

You can use a ReentrantLock.

That's exactly what it does.

Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
1

You can use ReentrantLock and a Condition to signal when the lock can be released.

cosmin.danisor
  • 943
  • 1
  • 12
  • 29
1

The JVM verifier is designed so that locks and unlocks must be balanced. So, you can't have unpaired lock and unlocks explicitly.

You could hold the lock in a different thread, so long as you are not relying upon reentrant behaviour (which always seems like a bad idea to me). Communicating with the other thread in a thread-safe manner will ensure you get the relevant happens-before relationships.

The standard way of achieving your goals is the Execute Around idiom, as used by javax.swing.text.AbstractDocument.render.

In Java SE 7 and earlier (not 1.0*), Execute Around would look like:

public interface Traversal<T>() {
    void traverse(Iterator<T> iter);
}
public class MyObj {
    private final Object lock = new Object();
    ...
    public void lockTraversal(Traversal<String> block) {
        synchronized (lock) {
            block.traverse(getAnIterator());
        }
    }
}
MyObj obj = (...)

obj.lockTraversal(new Traversal<String>() {
    public void traverse(Iterator<String> iter) {
        ...
    }
});

From Java SE 8, it's much the same except you can probably write something along the lines of:

obj.lockTraversal({iter -> // Guess type of iter.
    ...
});

As others have mentioned, java.util.concurrent.locks provides locks with fewer constraints. Note, losing those constraints may lead to subtle bugs. For instance, in the original question lockTraveral throwing may cause an unlock without a successful matching lock.

Community
  • 1
  • 1
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Alas the answer I didn't want to hear "you can't do that". To others, Yes I am fully aware that there are other locks and semaphores that can be used. The issue is I would like to have a way that is *compatible* ie: uses the *same* lock that synchronized uses under the hood so *there*is*only*one* and one can integrate with legacy code that uses synchronized. I would be sincerely interested in that answer :) – peterk Apr 10 '13 at 13:53
  • Of course ideal would be the iterator itself can maintain the lock, and then when it goes out of scope and is deleted ( not waiting for GC latency ) the lock would be released. Anther desirable way would be to have a closure where the block of code can be repeatedly executed inside the lock management factored out into the method receiving the closure doing the iteration. – peterk Apr 10 '13 at 13:59
0

Java has a separate locking framework which can be found in the java.util.concurrent.locks package since Java 1.5. It provides more extensive options than synchronized.

class X {
    private final ReentrantLock lock = new ReentrantLock();

    public void m() {
      lock.lock();  // block until condition holds
      try {
        // ... method body
      } finally {
        lock.unlock()
      }
    }
}
Tom Verelst
  • 15,324
  • 2
  • 30
  • 40