java - una - ¿Las cerraduras se pueden cerrar automáticamente?
llave maestra mercadolibre (8)
¿Son las cerraduras auto-reutilizables? Es decir, en lugar de:
Lock someLock = new ReentrantLock();
someLock.lock();
try
{
// ...
}
finally
{
someLock.unlock();
}
Puedo decir:
try (Lock someLock = new ReentrantLock())
{
someLock.lock();
// ...
}
en java 7?
El ReentrantLock
propósito ReentrantLock
no implementa ni proporciona nada que implemente la interfaz AutoCloseable
necesaria para una declaración try-with-resources. Sin embargo, el concepto no es completamente ajeno a la API de Java, ya que FileChannel.lock()
ofrece esta funcionalidad.
Las respuestas proporcionadas hasta ahora comparten soluciones que tienen algunos problemas, como crear un objeto innecesario en cada llamada de bloqueo, exponer una API propensa a errores o arriesgarse a fallar después de que se adquiere el bloqueo pero antes de que se ingrese en try-finally.
Solución de Java 7 :
public interface ResourceLock extends AutoCloseable {
/**
* Unlocking doesn''t throw any checked exception.
*/
@Override
void close();
}
public class CloseableReentrantLock extends ReentrantLock {
private final ResourceLock unlocker = new ResourceLock() {
@Override
public void close() {
CloseableReentrantLock.this.unlock();
}
};
/**
* @return an {@link AutoCloseable} once the lock has been acquired.
*/
public ResourceLock lockAsResource() {
lock();
return unlocker;
}
}
Solución Java 8 más fina utilizando un lambda:
public class CloseableReentrantLock extends ReentrantLock {
/**
* @return an {@link AutoCloseable} once the lock has been acquired.
*/
public ResourceLock lockAsResource() {
lock();
return this::unlock;
}
}
Demostración:
public static void main(String[] args) {
CloseableReentrantLock lock = new CloseableReentrantLock();
try (ResourceLock ignored = lock.lockAsResource()) {
try (ResourceLock ignored2 = lock.lockAsResource()) {
System.out.println(lock.getHoldCount()); // 2
}
}
System.out.println(lock.getHoldCount()); // 0
}
El try-with-resource
funciona bien para los recursos que se crean y destruyen cuando se deja try-block
. No funciona para los recursos que necesitan mantenerse vivos. Los bloqueos no se crean y se destruyen con cada uso. Se mantienen vivos y simplemente bloqueados y desbloqueados. Es por esto que no son AutoClosable
.
Como otros ya han sugerido try-with-resource
bloque try-with-resource
puede usar y envolver una envoltura y hacer el bloqueo y desbloqueo en la creación y destrucción.
Estaba buscando hacer esto yo mismo e hice algo como esto:
public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable {
public CloseableReentrantLock open() {
this.lock();
return this;
}
@Override
public void close() {
this.unlock();
}
}
y luego esto como uso para la clase:
public class MyClass {
private final CloseableReentrantLock lock = new CloseableReentrantLock();
public void myMethod() {
try(CloseableReentrantLock closeableLock = lock.open()) {
// locked stuff
}
}
}
No hay una solución perfecta, a menos que ignore los costos de asignación (la mayoría de los programadores de aplicaciones pueden, pero los escritores de la biblioteca de bloqueo no pueden). Entonces puedes usar una envoltura
public class AutoCloseableLockWrapper implements AutoCloseable, Lock{
private final Lock lock;
public AutoCloseableLockWrapper(Lock l) {
this.lock = l;
}
@Override
public void lock() {
this.lock.lock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
lock.lockInterruptibly();
}
@Override
public boolean tryLock() {
return lock.tryLock();
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return lock.tryLock(time,unit);
}
@Override
public void unlock() {
lock.unlock();
}
@Override
public Condition newCondition() {
return lock.newCondition();
}
@Override
public void close() {
this.lock.unlock();
}
}
en este constructo
@RequiredArgsConstructor(access=AccessLevel.PRIVATE)
public final class MgLockCloseable implements AutoCloseable {
public static MgLockCloseable tryLock(Lock lock) {
return new MgLockCloseable(lock.tryLock() ? lock : null);
}
public static MgLockCloseable lock(Lock lock) {
lock.lock();
return new MgLockCloseable(lock);
}
@Override public void close() {
if (isLocked()) {
lock.unlock();
}
}
public boolean isLocked() {
return lock != null;
}
@Nullable private final Lock lock;
}
Véase también mi pregunta sobre CR .
No, ni la interfaz Lock
(ni la clase ReentrantLock
) implementan la interfaz AutoCloseable
, que se requiere para usar con la nueva sintaxis try-with-resource.
Si quisiera que esto funcionara, podría escribir un envoltorio simple:
public class LockWrapper implements AutoCloseable
{
private final Lock _lock;
public LockWrapper(Lock l) {
this._lock = l;
}
public void lock() {
this._lock.lock();
}
public void close() {
this._lock.unlock();
}
}
Ahora puedes escribir código como este:
try (LockWrapper someLock = new LockWrapper(new ReentrantLock()))
{
someLock.lock();
// ...
}
Sin embargo, creo que es mejor que te quedes con la sintaxis antigua. Es más seguro tener su lógica de bloqueo completamente visible.
Sobre la base de la respuesta de Stephen y la idea de user2357112, he escrito la siguiente clase.
La clase MyLock en sí no se puede cerrar por sí misma, para obligar a los usuarios de la clase a llamar a get ().
public class MyLock {
public class Session implements AutoCloseable {
@Override
public void close() {
freeLock();
}
}
private ReentrantLock reentrantLock = new ReentrantLock();
public Session get() {
reentrantLock.lock();
return new Session();
}
private void freeLock() {
reentrantLock.unlock();
}
}
Aquí es un uso típico:
MyLock myLock = new MyLock();
try( MyLock.Session session = myLock.get() ) {
// Lock acquired
}
Teniendo en cuenta el astuto consejo de user2357112 :
public class CloseableLock {
private class Unlocker implements AutoCloseable {
@Override
public void close() throws Exception {
lock.unlock();
}
}
private final Lock lock;
private final Unlocker unlocker = new Unlocker();
public CloseableLock(Lock lock) {
this.lock = lock;
}
public AutoCloseable lock() {
this.lock.lock();
return unlocker;
}
}
Utilizar:
CloseableLock lock = new CloseableLock(new ReentrantLock());
try (AutoCloseable unlocker = lock.lock()) {
// lock is acquired, automatically released at the end of this block
} catch (Exception it) {
// deal with it
}
Podría ser interesante hacer que CloseableLock
implemente java.util.concurrent.locks.Lock
.
try (LockCloseable lockCloseable = LockCloseable.lock(lock)) {
doSomethingUnderLock();
} // automatic release