web-dev-qa-db-ja.com

ロックは自動クローズ可能ですか?

Locksは自動で閉じることができますか?つまり、次の代わりに:

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

...言ってもいい:

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

... in Java 7?

42
fredoverflow

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

ただし、古い構文を使用したほうがよいと思います。ロックロジックを完全に表示する方が安全です。

23
dlev

私はこれを自分で行うことを検討していて、次のようなことをしました。

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
      }
   }
}
56
Stephen

汎用 ReentrantLock は、try-with-resourcesステートメントに必要なAutoCloseableインターフェースを実装も提供もしません。 FileChannel.lock() がこの機能を提供するため、この概念はJava APIとは完全に異質ではありません。

これまでの回答は、ロック呼び出しごとに不要なオブジェクトを作成する、エラーが発生しやすい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;
    }
}

ラムダを使用したリーナー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
}
10
skoskav

try-with-resourceは、try-blockが残っているときに作成および破棄されるリソースに対して適切に機能します。存続させる必要のあるリソースでは機能しません。ロックは、使用するたびに作成および破棄されることはありません。それらは存続し、ロックおよびロック解除されるだけです。これが、それらがAutoClosableではない理由です。

他の人がすでに示唆しているように、ラッパーはtry-with-resourceブロックによって作成および破棄され、作成および破棄時にロックおよびロック解除を行うために使用できます。

6
Eduard Wirch

割り当てコストを無視しない限り、完璧な解決策はありません(ほとんどのアプリケーションプログラマーはできますが、ロックライブラリライターはできません)。次に、ラッパーを使用できます

@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

My CRに関する質問 も参照してください。

3
maaartinus
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();
    }
}
1
gstackoverflow

@skoskavのJava8ソリューションをReentrantReadWriteLockに拡張します。

public interface ResourceLock extends AutoCloseable {
    /**
     * Unlocking doesn't throw any checked exception.
     */
    @Override
    void close();
}    
public class CloseableReentrantRWLock extends ReentrantReadWriteLock {

    /**
     * @return an {@link AutoCloseable} once the ReadLock has been acquired
     */
    public ResourceLock lockRead() {
        this.readLock().lock();
        return () -> this.readLock().unlock();
    }

     /**
     * @return an {@link AutoCloseable} once the WriteLock has been acquired.
     */
    public ResourceLock lockWrite() {
        this.writeLock().lock();
        return () -> this.writeLock().unlock();
    }
} 
1
SomeUsername1

ser2357112の賢明なアドバイス を考慮に入れる:

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
}

CloseableLockJava.util.concurrent.locks.Lockを実装させるのは興味深いかもしれません。

1
sp00m

Stephenの答えと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
}
1
ocroquette

ロックとRunnableを使用する単純なutilメソッドは、ロックを使用してtry-with-resourceステートメントを使用するよりも優れていると思います。

このような:

public static void locked(Lock lock, Runnable r) {
    lock.lock();

    try {
        r.run();
    } finally {
        lock.unlock();
    }
}

使用例:

locked(lock, () -> {
    // Do your stuff
});

利点:

  • Try-with-resource用に作成されたダミー変数はありません。
  • とてもはっきりしていると思います。

不利益

  • Runnableインスタンスは呼び出しごとに割り当てられますが、これは他のソリューションの一部では回避されています。しかし、これはほとんどすべての場合に重要ではありません。
  • Java 8を使用できる場合にのみ機能します。
0
Lii