Являются ли блокировки AutoCloseable?
Проблема:
Я хотел бы узнать, являются ли объекты типа Lock
в Java автозакрываемыми? То есть, вместо следующего кода:
Lock someLock = new ReentrantLock();
someLock.lock();
try {
// ...
} finally {
someLock.unlock();
}
могу ли я написать:
try (Lock someLock = new ReentrantLock()) {
someLock.lock();
// ...
}
Возможно ли это в Java 7?
5 ответ(ов)
Вы можете реализовать класс, который будет расширять ReentrantLock
и реализовывать интерфейс AutoCloseable
, что позволит удобно управлять блокировкой с помощью конструкции try-with-resources
. Ваш код выглядит корректно и он правильно устанавливает и освобождает блокировку.
Вот пример вашего кода на русском:
public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable {
public CloseableReentrantLock open() {
this.lock();
return this;
}
@Override
public void close() {
this.unlock();
}
}
Использование этого класса в методе будет выглядеть следующим образом:
public class MyClass {
private final CloseableReentrantLock lock = new CloseableReentrantLock();
public void myMethod() {
try(CloseableReentrantLock closeableLock = lock.open()) {
// код, который выполняется в захваченной блокировке
}
}
}
Таким образом, когда вы вызываете метод myMethod
, блокировка будет взята на время выполнения кода внутри блока try
, и автоматически освобождена, когда выполнение выходит из этого блока. Это очень хороший способ управления ресурсами и предотвращения утечек, связанных с блокировками.
try-with-resource
отлично подходит для работы с ресурсами, которые создаются и уничтожаются по выходу из блока try
. Однако он не работает для ресурсов, которые должны оставаться активными на протяжении времени, такие как мьютексы. Замки не создаются и не уничтожаются при каждом использовании; они просто блокируются и разблокируются. Именно поэтому они не реализуют интерфейс AutoCloseable
.
Как уже предложили другие участники, можно использовать обертку, которая будет создаваться и уничтожаться в блоке try-with-resource
, выполняя блокировку и разблокировку при создании и уничтожении.
Я считаю, что простая утилитарная функция, которая принимает блокировку и Runnable
, лучше, чем использование конструкции try-with-resources для работы с блокировками.
Вот пример реализации:
public static void locked(Lock lock, Runnable r) {
lock.lock();
try {
r.run();
} finally {
lock.unlock();
}
}
Пример использования:
locked(lock, () -> {
// Ваш код
});
Преимущества:
- Не создаётся лишняя переменная для try-with-resources.
- Код выглядит очень понятно.
Недостаток:
- Для каждого вызова выделяется экземпляр
Runnable
, что некоторые другие решения избегают. Однако это несущественно практически в большинстве случаев. - Работает только в Java 8 и выше.
Ваш код выглядит как реализация обертки для объекта Lock
, который также реализует интерфейс AutoCloseable
. Это позволяет автоматически освобождать блокировку при использовании конструкции try-with-resources
. Вот перевод на русский в стиле ответа на StackOverflow:
Ваш код для класса AutoCloseableLockWrapper
реализует интерфейсы AutoCloseable
и Lock
, что позволяет использовать класс в конструкции try-with-resources
. Таким образом, блокировка будет автоматически освобождена при выходе из блока try
, даже если произойдет исключение.
Вот краткое описание элементов вашего кода:
Конструктор: Принимает объект
Lock
и сохраняет его.public AutoCloseableLockWrapper(Lock l) { this.lock = l; }
Методы lock() и unlock(): Вы просто делегируете вызовы методам объекта
Lock
, что позволяет управлять блокировками.@Override public void lock() { this.lock.lock(); } @Override public void unlock() { lock.unlock(); }
Методы для прерываемой блокировки: Реализованы методы
lockInterruptibly()
иtryLock()
, которые позволяют более гибко управлять блокировками, учитывая наличие прерываний.@Override public void lockInterruptibly() throws InterruptedException { lock.lockInterruptibly(); }
Реализация close(): В этом методе происходит автоматическое разблокирование, что делает ваш класс удобным для использования в конструкциях управления ресурсами.
@Override public void close() { this.lock.unlock(); }
Таким образом, ваш класс действительно полезен в случаях, когда нужно гарантировать, что блокировка будет освобождена автоматически, что предотвращает возможные проблемы с взаимными блокировками и утечками ресурсов. Если у вас есть дополнительные вопросы или хотите уточнений, пожалуйста, дайте знать!
Основываясь на ответе Стивена и идее пользователя user2357112, я написал следующий класс.
Класс MyLock
сам по себе не является закрываемым, чтобы заставить пользователей класса вызывать метод 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();
}
}
Вот типичное использование:
MyLock myLock = new MyLock();
try( MyLock.Session session = myLock.get() ) {
// Блокировка получена
}
Таким образом, использование класса MyLock
гарантирует, что блокировка будет освобождена корректно благодаря конструкции try-with-resources
, что делает управление ресурсами более безопасным и надежным.
Необходимый компилятор в данной среде отсутствует. Возможно, вы используете JRE вместо JDK?
Инициализация ArrayList в одну строчку
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Создание репозитория Spring без сущности
Как сгенерировать уникальный хеш-код для строкового ввода в Android?