Reputation: 73
I wanted to ask if this implementation is ok, or maybe there are some issues that can be later problematic.
The idea is to implement a ReentrantLock class that can be used with 'try with resources' and that means that it has to implement the AutoCloseable interface.
In addition, I wanted to ask why in the 'main' method the compiler yields that I have to add a 'catch' clause, because my close method (in MyReentrantLock) doesn't throw an exception.
import java.util.concurrent.locks.ReentrantLock;
public class Ex09ReentrantLock {
@SuppressWarnings("serial")
public static class MyReentrantLock extends ReentrantLock implements AutoCloseable{
public MyReentrantLock() {
super();
lock();
}
@Override
public void close(){
unlock();
}
}
public static AutoCloseable lock() {
var locker = new MyReentrantLock(); //lock() is called in the constructor
return locker; //implements AutoCloseable
}
//Demo
public static void main(String[] args) {
//try(ReentrantLock lock = new ReentrantLock()){} = compiler error
try(var locker = lock();){
//some work...
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Is this implementation ok? (call the lock in the constructor...)
Why does the compiler force me to add a catch() clause if my close doesn't throw an exception?
Upvotes: 0
Views: 366
Reputation: 15196
The AutoCloseable
interface defines the use of close handler which gets called automatically at the end of a try-with-resources block:
void close() throws Exception
You can adapt any method which has a compatible definition to the signature of close() as an AutoCloseable. An easy way to do this for ReentrantLock
(or Lock
) is to add an interface to perform lock()
and return unlock()
as the close handler. This interface defines a close() with generic exception type:
interface AutoClose<T extends Exception> extends AutoCloseable {
void close() throws T;
static AutoClose<RuntimeException> lock(Lock lock) {
lock.lock();
return lock::unlock;
}
}
Using the lock...unlock is then simply a try with resources call using an existing Lock
or ReentrantLock
:
ReentrantLock lock = new ReentrantLock();
...
try(var myLock = AutoClose.lock(lock)) {
// critical section here
}
It is easy to extend AutoClose
to handle other classes such as Semaphore
:
static AutoClose<RuntimeException> acquire(Semaphore semaphore) throws InterruptedException {
semaphore.acquire();
return semaphore::release;
}
// example call:
try(var sem = AutoClose.acquire(semaphore)) {
// critical section here
}
Upvotes: 0
Reputation: 1389
Locks are meant to be shared.
A lock that locks on construction can only be locked using new
and thus isn't shareable meaning its useless.
I believe your sample code is for illustration purpose only (but if not, it is definitely a bad idea), to help you more a real case should be given.
Guessing from what you shared, what's closest to what you expressed is the synchronized
keyword.
In general, locks interactions are difficult to handle, which led to the creation of the synchronized mechanism. Using locks instead should be done in very specific situations where synchronization isn't possible.
Upvotes: 1