Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why not using a try with lock in java?

I've read this topic, and this blog article about try with resources locks, as the question popped in my head. But actually, what I'd rather like would be a try with lock, I mean without lock instantiation. It would release us from the verbose

lock.lock();
try {
    //Do some synchronized actions throwing Exception 
} finally {
    //unlock even if Exception is thrown
    lock.unlock();
}

Would rather look like :

? implements Unlockable lock ;
...
try(lock) //implicitly calls lock.lock() 
{
    //Do some synchronized actions throwing Exception 
} //implicitly calls finally{lock.unlock();}

So it would not be a TWR, but just some boilerplate cleaning.

Do you have any technical reasons to suggest describing why this would not be a reasonable idea?

EDIT : to clarify the difference between what I propose and a simple synchronized(lock){} block, check this snippet :

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


public class Test {

        public static void main(String[] args) {
                ReentrantLock locker =new ReentrantLock();
                Condition condition = locker.newCondition();
                Thread t1 = new Thread("Thread1") {
                        @Override
                        public void run(){
                                synchronized(locker){
                                        try {
                                                condition.await();
                                        } catch (InterruptedException e) {
                                                Thread.currentThread().interrupt();
                                        }
                                        System.out.println("Thread1 finished");
                                }
                        }
                } ;
                Thread t2 = new Thread("Thread2") {
                        @Override
                        public void run(){
                                synchronized(locker){
                                        Thread.yield();
                                        condition.signal();
                                        System.out.println("blabla2");
                                }
                        }
                } ;
                t1.start();
                t2.start();
        }

}

Execution will result in a IllegalMonitorStateException, so lock() and unlock() methods are not implicitly called within synchronized block.

like image 795
Jules Sam. Randolph Avatar asked Sep 29 '22 14:09

Jules Sam. Randolph


1 Answers

If you had to deal with a simple case like that, where the pattern of locking/unlocking was limited to a narrow scope like this, you probably don't want to use the more complicated Lock class and probably should just be using the synchronized keyword, instead. That being said, if for some reason you needed this with the more complicated Lock object, it should be relatively straight-forward to create a wrapper around Lock that implements the AutoCloseable interface to be able to do just that. Example:

class AutoUnlock implements AutoCloseable {
  private final Lock lock;

  public static AutoUnlock lock(Lock lock) {
    lock.lock();
    return new AutoUnlock(lock);
  }

  public static AutoUnlock tryLock(Lock lock) {
    if (!lock.tryLock()) {
       throw new LockNotAcquiredException();
    }
    return new AutoUnlock(lock);
  }

  @Override
  public void close() {
    lock.unlock();
  }

  private AutoUnlock(Lock lock) {
    this.lock = lock;
  }
}

With a wrapper like the above, you could then do:

try (AutoUnlock autoUnlock = AutoUnlock.lock(lock)) {
  // ... do whatever that requires the lock ...
}

That being said, the Lock class is typically used for very complicated locking scenarios where this wouldn't be particularly useful. For example, Lock objects may be locked in one function in a class and later unlocked in another function (e.g. locking a row in a database in response to an incoming remote procedure call, and then unlocking that row in response to a later RPC), and thus having such a wrapper or making a Lock AutoCloseable, itself, would be of limited use for the way it is actually used. For more simple scenarios, it's more common to just use an existing concurrent datastructure or use synchronized.

like image 149
Michael Aaron Safyan Avatar answered Oct 02 '22 14:10

Michael Aaron Safyan