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.
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.
This answer serves to explain the behavior of your edit. The purpose of synchronized
is to lock the monitor of the given object when the thread enters the block (waiting if it isn't available) and releasing it when the thread exits the block.
Lock
is a higher level abstraction.
Lock
implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.
You can use it to lock across method boundaries. synchronized
is not able to do this so a Lock
cannot be implemented solely with synchronized
and no implementation I've ever seen uses it. Instead, they use other patterns, like compare and swap. They use this to set a state atomically within a Lock
object which marks a certain thread as the owner of the lock.
In your code snippet, you try to invoke
condition.signal();
in a thread which does not own the Lock
from which the condition
was created. The javadoc states
An implementation may (and typically does) require that the current thread hold the lock associated with this
Condition
when this method is called. Implementations must document this precondition and any actions taken if the lock is not held. Typically, an exception such asIllegalMonitorStateException
will be thrown.
That's what happened here.
Executing
synchronized (lock) {}
makes the current thread lock (and then release) the monitor on the object referenced by lock
. Executing
lock.lock();
makes the current thread set some state within the object referenced by lock
which identifies it as the owner.
来源:https://stackoverflow.com/questions/26435639/why-not-using-a-try-with-lock-in-java