0

Являются ли блокировки AutoCloseable?

12

Проблема:

Я хотел бы узнать, являются ли объекты типа Lock в Java автозакрываемыми? То есть, вместо следующего кода:

Lock someLock = new ReentrantLock();
someLock.lock();
try {
    // ...
} finally {
    someLock.unlock();
}

могу ли я написать:

try (Lock someLock = new ReentrantLock()) {
    someLock.lock();
    // ...
}

Возможно ли это в Java 7?

5 ответ(ов)

0

Вы можете реализовать класс, который будет расширять 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, и автоматически освобождена, когда выполнение выходит из этого блока. Это очень хороший способ управления ресурсами и предотвращения утечек, связанных с блокировками.

0

try-with-resource отлично подходит для работы с ресурсами, которые создаются и уничтожаются по выходу из блока try. Однако он не работает для ресурсов, которые должны оставаться активными на протяжении времени, такие как мьютексы. Замки не создаются и не уничтожаются при каждом использовании; они просто блокируются и разблокируются. Именно поэтому они не реализуют интерфейс AutoCloseable.

Как уже предложили другие участники, можно использовать обертку, которая будет создаваться и уничтожаться в блоке try-with-resource, выполняя блокировку и разблокировку при создании и уничтожении.

0

Я считаю, что простая утилитарная функция, которая принимает блокировку и 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 и выше.
0

Ваш код выглядит как реализация обертки для объекта Lock, который также реализует интерфейс AutoCloseable. Это позволяет автоматически освобождать блокировку при использовании конструкции try-with-resources. Вот перевод на русский в стиле ответа на StackOverflow:


Ваш код для класса AutoCloseableLockWrapper реализует интерфейсы AutoCloseable и Lock, что позволяет использовать класс в конструкции try-with-resources. Таким образом, блокировка будет автоматически освобождена при выходе из блока try, даже если произойдет исключение.

Вот краткое описание элементов вашего кода:

  1. Конструктор: Принимает объект Lock и сохраняет его.

    public AutoCloseableLockWrapper(Lock l) {
        this.lock = l;
    }
    
  2. Методы lock() и unlock(): Вы просто делегируете вызовы методам объекта Lock, что позволяет управлять блокировками.

    @Override
    public void lock() {
        this.lock.lock();
    }
    
    @Override
    public void unlock() {
        lock.unlock();
    }
    
  3. Методы для прерываемой блокировки: Реализованы методы lockInterruptibly() и tryLock(), которые позволяют более гибко управлять блокировками, учитывая наличие прерываний.

    @Override
    public void lockInterruptibly() throws InterruptedException {
        lock.lockInterruptibly();
    }
    
  4. Реализация close(): В этом методе происходит автоматическое разблокирование, что делает ваш класс удобным для использования в конструкциях управления ресурсами.

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

Таким образом, ваш класс действительно полезен в случаях, когда нужно гарантировать, что блокировка будет освобождена автоматически, что предотвращает возможные проблемы с взаимными блокировками и утечками ресурсов. Если у вас есть дополнительные вопросы или хотите уточнений, пожалуйста, дайте знать!

0

Основываясь на ответе Стивена и идее пользователя 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, что делает управление ресурсами более безопасным и надежным.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь