Подтвердить что ты не робот

Являются ли замки AutoCloseable?

Являются ли блокировки AutoCloseable? То есть вместо:

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

могу сказать:

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

в Java 7?

4b9b3361

Ответ 1

Нет, ни интерфейс Lock, ни ReentrantLock) реализует интерфейс AutoCloseable, который требуется для использования с новым синтаксисом try-with-resource.

Если вы хотите заставить это работать, вы можете написать простую оболочку:

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();
    }
}

Теперь вы можете написать код следующим образом:

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

Я думаю, вам лучше придерживаться старого синтаксиса. Это безопаснее, чтобы ваша логика блокировки была полностью видна.

Ответ 2

Я занимался этим сам и делал что-то вроде этого:

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()) {
         // locked stuff
      }
   }
}

Ответ 3

try-with-resource хорошо работает для ресурсов, которые создаются и уничтожаются, когда остается try-block. Он не работает для ресурсов, которые необходимо сохранить в живых. Замки не создаются и не уничтожаются при каждом использовании. Они остаются в живых и просто заблокированы и разблокированы. Вот почему они не AutoClosable.

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

Ответ 4

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();
    }
}

Ответ 5

Нет идеального решения, если вы не игнорируете затраты на размещение (большинство программистов приложений могут, но разработчики библиотеки блокировки не могут). Затем вы можете использовать обертку

@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;
}

в этой конструкции

try (LockCloseable lockCloseable = LockCloseable.lock(lock)) {
    doSomethingUnderLock();
} // automatic release

См. также мой вопрос в CR.

Ответ 6

Взяв 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;
  }

}

Использование:

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
}

Может быть интересно сделать CloseableLock реализацию java.util.concurrent.locks.Lock.

Ответ 7

На основе ответа Стивена и идеи 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() ) {
    // Lock acquired
}

Ответ 8

Универсальный ReentrantLock не реализует и не предоставляет ничего, что реализует интерфейс AutoCloseable, необходимый для try-in-resources выражение. Концепция не является полностью чуждым API Java, хотя FileChannel.lock() предлагает эту функциональность.

Ответы, предоставленные до сих пор, совместно используют решения, которые имеют некоторые проблемы, такие как создание ненужного объекта при каждом вызове блокировки, предоставление API с ошибкой или отказ от риска после того, как блокировка будет получена, но до того, как будет введена попытка try./p >

Решение 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;
    }
}

Leaner Решение Java 8 с использованием лямбда:

public class CloseableReentrantLock extends ReentrantLock {

    /**
     * @return an {@link AutoCloseable} once the lock has been acquired.
     */
    public ResourceLock lockAsResource() {
        lock();
        return this::unlock;
    }
}

Демонстрация:

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
}