Замки автоматически закрываются? - PullRequest
36 голосов
/ 06 августа 2011

Замки автоматически закрываются?То есть вместо:

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

можно сказать:

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

в Java 7?

Ответы [ 8 ]

48 голосов
/ 12 июня 2012

Я пытался сделать это сам и сделал что-то вроде этого:

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
      }
   }
}
19 голосов
/ 06 августа 2011

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

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

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

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

5 голосов
/ 16 сентября 2017

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

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

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
}
4 голосов
/ 06 марта 2014

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

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

2 голосов
/ 25 мая 2016

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

@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 .

0 голосов
/ 22 февраля 2017

Основываясь на ответе Стивена и идее 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
}
0 голосов
/ 23 января 2017

Принимая во внимание проницательный совет пользователя2357112 :

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.

0 голосов
/ 06 августа 2015
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();
    }
}
...